T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/skill_router.py:49
- Finding
- API Key Disclosure Through Unvalidated Cross-Origin Redirects## Vulnerability Details **File Location**: `scripts/skill_router.py`, lines 49-67 **Vulnerability Type**: Outbound request redirect validation bypass and credential exposure **Risk Level**: Medium ### Vulnerable Code ```python # Restrict outbound requests to the expected Media.io API host. parsed = urlparse(url) if parsed.scheme != 'https' or parsed.netloc.lower() != 'openapi.media.io': return {'error': f"Blocked endpoint host: {parsed.netloc}"} headers = { 'X-API-KEY': resolved_api_key, 'Content-Type': 'application/json' } # Replace path parameters in endpoint URLs. if '{' in url: for k, v in params.items(): url = url.replace(f'{{{k}}}', str(v)) # Keep non-path parameters in the JSON body. body = {k: v for k, v in params.items() if f'{{{k}}}' not in api['endpoint']} try: resp = requests.request(method, url, headers=headers, json={'data': body} if body else {}, timeout=30) ``` ### Technical Analysis The implementation verifies that the initial endpoint uses HTTPS and has the exact hostname `openapi.media.io`. It then sends the request using `requests.request` without disabling redirects. The Python `requests` library follows redirects by default. The code does not inspect or validate redirect destinations before following them. Although `requests` has special handling for standard authorization headers, the credential is supplied through the custom `X-API-KEY` header. Such custom headers can remain attached to a redirected request, including a redirect to a different origin. Consequently, the initial host allowlist does not guarantee that the API key is sent exclusively to `openapi.media.io`. A cross-origin redirect returned by the accepted endpoint could cause the request and its sensitive header to be delivered to another host. Exploitation depends on the trusted endpoint returning an attacker-controlled cross-origin redirect, such as through server compromise, an upstre ...[truncated 1444 chars]
- Remediation
- ## Remediation Suggestions Disable automatic redirects for authenticated API requests: ```python resp = requests.request( method, url, headers=headers, json={'data': body} if body else {}, timeout=30, allow_redirects=False, ) ``` Treat unexpected redirect responses as errors. If redirects are operationally required, implement controlled redirect handling: 1. Follow redirects manually. 2. Parse and normalize every redirect destination. 3. Require the destination scheme to remain `https`. 4. Require the destination hostname to remain exactly `openapi.media.io`. 5. Reject user-information components, nonstandard ports, malformed hosts, and protocol-relative destinations. 6. Set a small maximum redirect count to prevent loops. 7. Remove `X-API-KEY` before any request whose origin differs from the original origin. 8. Add automated tests covering `301`, `302`, `303`, `307`, and `308` redirects to both approved and unapproved hosts. A hardened implementation should also validate the fully substituted URL immediately before transmission so that the URL receiving the credential is always subject to the final allowlist check.
