T09 · Insecure Skill Coding Practices
Error
- Location
- utils/image_utils.py:13
- Finding
- Server-Side Request Forgery Through Unrestricted Image URLs<![CDATA[ ## Vulnerability Details **File Location**: `api/routes.py:81-85`; `utils/image_utils.py:13-19` **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python # api/routes.py:81-85 if req.image_base64: img = load_image_from_base64(req.image_base64) else: img = await load_image_from_url(req.image_url) ``` ```python # utils/image_utils.py:13-19 async def load_image_from_url(url: str) -> Image.Image: """Download and load an image from a URL.""" async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url) resp.raise_for_status() return Image.open(io.BytesIO(resp.content)).convert("RGBA") ``` ### Technical Analysis The API accepts a caller-controlled URL and passes it directly to `httpx.AsyncClient.get`. It does not restrict URL schemes or destination hosts, resolve and validate destination addresses, block private and reserved networks, or validate redirect targets. An attacker can therefore make the application issue requests from the server's network context. Potential destinations include loopback services, private network hosts, link-local addresses, and cloud instance metadata endpoints. The response must ultimately be parseable as an image for the entire image-processing workflow to succeed, but the initial network request occurs regardless, allowing network probing and interaction with image-returning internal services. Redirects are not explicitly enabled in this client construction, which limits redirect-based bypasses under the relevant `httpx` defaults, but it does not mitigate direct requests to prohibited destinations. ### Attack Path 1. An attacker sends a request to `/api/skill/alpha-equip`. 2. The `image_url` field is set to an internal or otherwise inaccessible destination, such as a loopback, private-network, or link-local URL. 3. The application passes the value directly to `httpx`. 4. The server connects to the destinati ...[truncated 835 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions The safest option is to remove server-side URL ingestion and accept only directly uploaded image data. If URL ingestion is required: 1. Accept only `https` URLs. 2. Parse URLs with a strict URL parser and reject embedded credentials, malformed hosts, and unexpected ports. 3. Resolve the hostname before connecting. 4. Reject every resolved loopback, private, link-local, multicast, reserved, unspecified, and documentation address for both IPv4 and IPv6. 5. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 6. Disable redirects or validate the destination of every redirect using the same policy. 7. Prefer an explicit allowlist of trusted image-hosting domains. 8. Route downloads through a network-isolated proxy with no access to internal services or metadata endpoints. 9. Return generic client errors rather than raw network exceptions that can reveal internal connectivity details. 10. Add tests for loopback, RFC1918, IPv6 local, integer-encoded IP, alternate address notation, and DNS-rebinding cases. ]]>
