T09 · Insecure Skill Coding Practices
Warning
- Location
- track.py:10
- Finding
- Unrestricted API Base URL Exposes Credentials and Shipment Data<![CDATA[ ## Vulnerability Details **File Location**: `track.py:10-38` **Vulnerability Type**: Server-Side Request Forgery and sensitive-data disclosure through an unvalidated configurable endpoint **Risk Level**: Medium ### Vulnerable Code ```python def get_api_base() -> str: return os.getenv("TRACK123_API_BASE", "https://api.track123.com/gateway/open-api/tk/v2") def get_api_secret() -> str: secret = os.getenv("TRACK123_API_SECRET") if not secret: raise RuntimeError("TRACK123_API_SECRET not set") return secret def api_headers() -> Dict[str, str]: return { "Track123-Api-Secret": get_api_secret(), # Track123 Header[web:35] "accept": "application/json", "content-type": "application/json", } def query_track123(tracking_number: str, postal_code: str | None) -> Dict[str, Any]: """ Track123 /track/query – auto-detect mit leerem courierCode.[web:35] """ url = f"{get_api_base()}/track/query" payload = { "trackNos": [tracking_number], "orderNos": [""], "queryPageSize": 1, } if postal_code: payload["postalCode"] = postal_code # Für Filterung/Erweiterung[web:35] resp = requests.post(url, headers=api_headers(), json=payload) ``` ### Technical Analysis The `TRACK123_API_BASE` environment variable is accepted without validating its URL scheme, hostname, port, or resolved destination. The application then unconditionally attaches the `TRACK123_API_SECRET` header and submits the user's tracking number and optional postal code to that destination. An attacker who can influence the process environment or deployment configuration can set the base URL to an attacker-controlled server. The code also accepts plaintext HTTP URLs, allowing the API secret and shipment data to be transmitted without transport encryption. Because `requests.post()` follows redirects by default, redirect handling should also be constrained so the custom secret header cannot re ...[truncated 1849 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Enforce HTTPS** - Parse the configured URL with `urllib.parse.urlparse`. - Reject every scheme other than `https`. - Reject URLs containing embedded credentials. 2. **Allowlist trusted destinations** - Prefer a fixed Track123 endpoint in production. - If configuration is necessary, require the normalized hostname to exactly match `api.track123.com`. - Validate the effective port and reject unexpected ports. - Do not rely on suffix matching such as `endswith("track123.com")`, which can accept attacker-controlled lookalike domains. 3. **Constrain redirects** - Disable automatic redirects with `allow_redirects=False`. - Alternatively, inspect each redirect target and resend the credential only when the destination remains on the exact trusted HTTPS origin. 4. **Reduce credential exposure** - Attach `Track123-Api-Secret` only after the destination has passed validation. - Use a narrowly scoped API credential where the provider supports scopes. - Rotate the credential immediately if an untrusted endpoint may previously have been configured. 5. **Add network-level restrictions** - Restrict outbound traffic for the skill process to the official Track123 API host. - Block access to loopback, link-local, private, and cloud metadata addresses where feasible. 6. **Harden request handling** - Add explicit connection and read timeouts. - Place reasonable limits on response sizes. - Return sanitized errors that do not expose credentials or unnecessary endpoint details. A secure implementation should validate the origin before constructing headers, for example: ```python from urllib.parse import urlparse TRUSTED_HOST = "api.track123.com" def get_api_base() -> str: base = os.getenv( "TRACK123_API_BASE", "https://api.track123.com/gateway/open-api/tk/v2", ).rstrip("/") parsed = urlparse(base) if ( parsed.scheme != "https" or parsed.ho ...[truncated 385 chars]
