T09 · Insecure Skill Coding Practices
- Location
scripts/wechat_article.py:169- Finding
Weak URL Validation Enables Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
scripts/wechat_article.py:56-69, 169-191
Vulnerability Type: Server-Side Request Forgery through insufficient URL validation
Risk Level: HighVulnerable Code
python def fetch_via_web_fetch(url: str) -> dict: """ Directly fetch webpage content. """ headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", } try: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() html = response.textpython def wechat_article(url: str) -> dict: """ Main function for retrieving a WeChat article. """ # Validate URL if not url or "mp.weixin.qq.com" not in url: return { "success": False, "error": "Invalid WeChat article URL" } # Strategy 1: Use jina.ai result = fetch_via_jina(url) if result["success"]: parsed = parse_jina_content(result["content"]) return { "success": True, "method": result["method"], "title": parsed["title"], "content": parsed["content"], "publish_time": parsed["publish_time"], "account": parsed["account"], "url": url } # Strategy 2: Fetch directly result = fetch_via_web_fetch(url)Technical Analysis
The application validates an untrusted URL by checking only whether the literal substring
mp.weixin.qq.comappears anywhere in the supplied value. This does not establish that the parsed destination hostname is actuallymp.weixin.qq.com.An attacker can place the trusted hostname in an irrelevant URL component, such as the user-information section or query string. For exa ...[truncated 2541 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse the URL with
urllib.parse.urlsplit()and reject malformed URLs. - Require the
httpsscheme. - Compare the normalized parsed hostname against an explicit allowlist. Require exactly
mp.weixin.qq.com, or explicitly enumerate any approved subdomains instead of using substring or suffix matching. - Reject URLs containing user information, unexpected ports, fragments, or ambiguous host encodings.
- Resolve the hostname before making a request and reject every address that is loopback, private, link-local, reserved, multicast, or unspecified using Python's
ipaddressmodule. - Protect against DNS rebinding by ensuring the validated address is the address used for the connection.
- Disable automatic redirects with
allow_redirects=False, or manually process redirects and apply the complete validation procedure to every destination. - Apply the same validation before sending a URL to the third-party Jina service.
- Enforce response-size and content-type limits to reduce the impact of malicious or unexpectedly large responses.
- Add regression tests covering user-information URLs, query-string bypasses, encoded hostnames, IPv4 and IPv6 loopback addresses, private ranges, link-local metadata addresses, alternate ports, and redirect chains.
- Parse the URL with
