T09 · Insecure Skill Coding Practices
Note
- Location
- scripts/daily_scanner.py:33
- Finding
- API Credential Exposed in Query String by Daily Scanner<![CDATA[ ## Vulnerability Details **File Location**: `scripts/daily_scanner.py:33-40` **Vulnerability Type**: API credential exposure through URL query parameters **Risk Level**: Low ### Vulnerable Code ```python params = { "apiKey": API_KEY, "regions": regions, "markets": markets, "dateFormat": "iso", "oddsFormat": "decimal", } url = f"{BASE_URL}/sports/{sport_key}/odds?" + urllib.parse.urlencode(params) try: with urllib.request.urlopen(url, timeout=15) as resp: ``` ### Technical Analysis The script retrieves `ODDS_API_KEY` from the environment and inserts it into the request URL as the `apiKey` query parameter. The request uses HTTPS and is sent only to the declared host, `api.the-odds-api.com`, so this behavior is consistent with the Skill's stated odds-retrieval functionality and is not evidence of intentional credential exfiltration. However, query-string credentials are exposed to more infrastructure components than credentials sent in headers. Complete URLs may be retained by API access logs, reverse proxies, observability products, error-reporting systems, HTTP debugging tools, or process diagnostics. Anyone able to read such records could recover and reuse the key. The network destination and requested access do not exceed the Skill's declared privileges. The weakness concerns how the necessary privilege is exercised. ### Attack Path 1. The user or Skill platform injects `ODDS_API_KEY` into the process environment. 2. The script appends the key to the outgoing request URL as `apiKey=<secret>`. 3. A server-side access log, proxy, monitoring service, or diagnostic mechanism records the full URL. 4. An attacker or unauthorized operator obtains read access to that record. 5. The attacker extracts the API key from the query string. 6. The attacker reuses the key with The Odds API until it is revoked, rotated, or its quota is exhausted. This path requires access to URL-bearing logs or diagnostics; the audited code does not ...[truncated 543 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Prefer an authorization header if The Odds API supports header-based authentication: ```python query = urllib.parse.urlencode({ "regions": regions, "markets": markets, "dateFormat": "iso", "oddsFormat": "decimal", }) url = f"{BASE_URL}/sports/{sport_key}/odds?{query}" request = urllib.request.Request( url, headers={"Authorization": f"Bearer {API_KEY}"}, ) with urllib.request.urlopen(request, timeout=15) as resp: ... ``` 2. If the provider mandates the `apiKey` query parameter: - Never print or persist the complete request URL. - Configure clients, proxies, monitoring systems, and server logs to redact `apiKey`. - Restrict access to network and application logs. - Minimize log retention. - Ensure exception-reporting tools scrub URL query strings. 3. Rotate the key if it may already have appeared in logs or diagnostics. 4. Use a narrowly scoped, quota-limited API key where the provider supports such controls. 5. Add automated tests that verify application logs and raised errors do not contain the key. ]]>
