T09 · Insecure Skill Coding Practices
- Location
scripts/jd_link_converter.py:41- Finding
Unvalidated Redirect Following Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/jd_link_converter.py, lines 41-93
Vulnerability Type: Server-Side Request Forgery through unvalidated redirect destinations
Risk Level: MediumThe converter restricts the initial short URL to
3.cnoru.jd.com, but it does not apply equivalent validation to subsequent redirect destinations.python current_url = url for _ in range(max_redirects): try: req = urllib.request.Request( current_url, method="GET", headers={ "User-Agent": mobile_ua, "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9", }, ) # Do not automatically follow redirects; handle each hop manually class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return None opener = urllib.request.build_opener(NoRedirect, urllib.request.HTTPHandler) try: resp = opener.open(req, timeout=10) final_url = resp.url resp.close() # Return immediately if a product page has been reached if "item.m.jd.com" in final_url or "item.jd.com" in final_url: return final_url current_url = final_url except urllib.error.HTTPError as e: if e.code in (301, 302, 303, 307, 308): location = e.headers.get("Location", "") if location: # Handle relative paths if location.startswith("/"): from urllib.parse import urljoin location = urljoin(current_url, location) if "item.m.jd.com" in location or "item.jd.com" in location: retur ...[truncated 3725 chars]- Remediation
View remediation
Remediation Suggestions
- Parse the initial URL and every redirect destination with
urllib.parse.urlsplit; never validate URLs using substring checks. - Require HTTPS for the initial URL and all redirects.
- Allowlist the exact hostnames required for the conversion workflow, such as
3.cn,u.jd.com,item.m.jd.com, anditem.jd.com. Reject user-info components, unexpected ports, malformed hostnames, and hostname suffix tricks. - Resolve every destination hostname before making a request. Reject all loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 addresses using Python's
ipaddressmodule. - Revalidate every redirect hop, including relative redirects after resolving them with
urljoin. - Prevent DNS rebinding by connecting only to an already validated resolved address where practical, while preserving and separately validating the intended HTTP host and TLS identity.
- Remove the automatic-redirect fallback or use the same no-redirect handler and validation routine for every hop.
- Restrict destination ports to the expected HTTPS port and retain strict redirect-count and timeout limits.
- Add tests covering redirects to
127.0.0.1,::1, RFC1918 networks, link-local ranges such as169.254.0.0/16, encoded or alternative IP representations, non-HTTPS schemes, hostname suffix tricks, and redirect chains that become unsafe after an initially valid hop. - Run the converter in a network-restricted sandbox that cannot reach cloud metadata services or internal administrative networks.
- Parse the initial URL and every redirect destination with
