T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/test_nano_banana_2.py:62
- Finding
- API Key Disclosure and SSRF Through Untrusted Image Result URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test_nano_banana_2.py:62-66`, with the credential-bearing client created at `scripts/test_nano_banana_2.py:344` **Vulnerability Type**: Credential disclosure through an untrusted URL and server-side request forgery **Risk Level**: High ### Vulnerable Code ```python def _extract_image_bytes(item: dict[str, Any], client: httpx.Client) -> bytes: if "b64_json" in item and isinstance(item["b64_json"], str): return base64.b64decode(item["b64_json"]) url_value = item.get("url") if isinstance(url_value, str) and url_value: resp = client.get(url_value, timeout=60) resp.raise_for_status() return resp.content raise RuntimeError("响应中未包含 b64_json 或 url") ``` The client passed to this function is initialized with the API key as a default header: ```python with httpx.Client(headers=_build_headers(api_key), follow_redirects=True) as client: ``` The header construction is: ```python def _build_headers(api_key: str) -> dict[str, str]: return {"Authorization": f"Bearer {api_key}"} ``` ### Technical Analysis The image-generation service controls the `url` field returned in its JSON response. The script performs a GET request to that URL using the same `httpx.Client` that has the API key configured as a default `Authorization` header. No validation is performed on the URL scheme, hostname, resolved IP address, port, or redirect destination. Consequently, a compromised or malicious API response can direct the client to: - An attacker-controlled HTTPS endpoint, potentially receiving the bearer credential. - Loopback or private-network services. - Link-local cloud metadata services. - Unexpected non-image resources. - Redirect chains whose destinations have not been independently validated. The request also downloads the complete response into memory without enforcing an explicit size limit. This creates an additional resource-exhaustion risk if the destinatio ...[truncated 1584 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Use a separate HTTP client without authentication headers for image downloads: ```python with httpx.Client( follow_redirects=False, timeout=httpx.Timeout(60), ) as download_client: image_bytes = _extract_image_bytes(item, download_client) ``` 2. Require the `https` scheme and allowlist the exact image-delivery domains expected from the service. 3. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses. 4. Disable redirects or manually validate every redirect target before following it. 5. Never copy the API `Authorization` header to image-download requests. 6. Stream response bodies and enforce a conservative maximum download size. 7. Validate `Content-Type` and verify the downloaded bytes are a supported image format before writing them. 8. Prefer Base64 image data in the authenticated API response where the service supports it, avoiding secondary URL retrieval entirely. ]]>
