T09 · Insecure Skill Coding Practices
- Location
scripts/chinese_zodiac_query.py:22- Finding
AppKey Disclosure Through an Unvalidated API Endpoint Override
- Content
View full analysis
dict[str, Any]: """ 功能说明: 1. 调用即刻数据开放接口。 2. 自动追加 `appkey` 参数。 3. 返回接口 JSON;网络异常时返回统一错误结构。 @param params 接口业务参数 @param appkey 即刻数据 AppKey @return dict 接口返回或错误结构 """ url = f"{API_BASE_URL}{API_PATH}?{urllib.parse.urlencode({**params, 'appkey': appkey})}" try: with urllib.request.urlopen(url, timeout=15) as response: return json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return {"code": exc.code, "message": f"接口请求失败: HTTP {exc.code}", "data": ""} except urllib.error.URLError as exc: return {"code": 500, "message": f"网络请求失败: {exc.reason}", "data": ""} except Exception as exc: return {"code": 500, "message": f"请求异常: {exc}", "data": ""} ``` ### Technical Analysis The script permits the API base URL to be replaced through the undocumented `JIKE_API_BASE_URL` environment variable. It does not validate the resulting URL's scheme, hostname, port, or destination before appending the AppKey and issuing the request. Consequently, any party able to influence the process environment can redirect the request from the intended `https://api.jikeapi.cn` service to an arbitrary endpoint. The configured AppKey is included in the query string as `appkey`, so the substituted server receives the credential directly. Query-string credentials may additionally be retained in HTTP server logs, reverse-proxy logs, monitoring systems, or diagnostic records. The override also permits non-HTTPS destinations because no scheme restriction is enforced. If a ...[truncated 1603 chars]- Remediation
View remediation
str: parsed = urlparse(value) if parsed.scheme != "https": raise ValueError("The API endpoint must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise ValueError("The API endpoint host is not allowed") if parsed.username or parsed.password or parsed.query or parsed.fragment: raise ValueError("The API base URL contains unsupported components") if parsed.port not in (None, 443): raise ValueError("The API endpoint port is not allowed") return value.rstrip("/") ``` 3. Keep production and test endpoint selection in trusted configuration rather than inheriting an unrestricted environment value. If test endpoints are necessary, use an explicit development-only mode and a separate non-production credential. 4. Prefer sending the AppKey in an authorization header rather than in the URL query string if the API provider supports it. This reduces exposure through URL logs and monitoring systems. 5. Ensure diagnostic output, exceptions, proxy logs, and HTTP access logs do not record credentials. Rotate the AppKey if the Skill may previously have run with an untrusted endpoint configuration. 6. Add automated tests confirming that non-HTTPS URLs, unapproved hostnames, embedded credentials, unexpected ports, redirects to unapproved hosts, and malformed URLs are rejected before any credential-bearing request is sent. ]]>
