T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/wait_for_task.py:34
- Finding
- Long-Lived API Token Transmitted in URL Query Parameters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wait_for_task.py:34-38, 103-108, 130-136` **Duplicated Location**: `_dependencies/skills/dataify-task-operations/scripts/wait_for_task.py` **Vulnerability Type**: Credential exposure through URL query parameters **Risk Level**: Medium ### Vulnerable Code ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request(url, method="GET") try: with urllib.request.urlopen(request, timeout=timeout) as response: ``` The function is invoked with the API token in `params`: ```python payload = request_json( STATUS_ENDPOINT, {"api_key": api_key, "task_id": task_id}, api_key, request_timeout, ) ``` The same behavior occurs when downloading results: ```python return request_json( DOWNLOAD_ENDPOINT, {"api_key": api_key, "task_id": task_id, "type": "json"}, api_key, request_timeout, ) ``` ### Technical Analysis The polling implementation places `DATAIFY_API_TOKEN` in the query string for both task-status and result-download requests. Although the endpoints use HTTPS, URL query strings can be retained in server access logs, reverse-proxy logs, monitoring systems, network diagnostics, browser-like URL histories, and error reports. The response redaction performed by the function does not protect the request URL: ```python if api_key: text = text.replace(api_key, "<redacted>") ``` This replacement only removes the token if it appears in the response body. It cannot remove copies retained by infrastructure before the response is processed. Sending authentication credentials as an `Authorization` header is the minimum-privilege design already used by other project components. Query-string authentication unnecessarily increases the number of systems that may observe or retain the credential. ### Attack Path 1. A user configures a valid, long-lived `DATAIFY_API_T ...[truncated 1006 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove `api_key` from all query parameters. 2. Send the token exclusively in an authorization header: ```python def request_json(endpoint, params, api_key, timeout): url = endpoint + "?" + urllib.parse.urlencode(params) request = urllib.request.Request( url, headers={"Authorization": "Bearer {}".format(api_key)}, method="GET", ) ``` 3. Keep only non-secret values such as `task_id` and `type` in the URL. 4. Use an explicit redirect policy. Do not forward the authorization header if a redirect changes the scheme, hostname, or port. 5. Redact tokens from exception messages and diagnostic output in addition to response bodies. 6. Rotate any token that may already have appeared in retained access logs. 7. Apply the same correction to the duplicated dependency implementation. ]]>
