T09 · Insecure Skill Coding Practices
Error
- Location
- invoice-discern.py:14
- Finding
- Unrestricted API Endpoint Allows Disclosure of Credentials and Invoice Data## Vulnerability Details **File Location**: `invoice-discern.py`, lines 14–25 and 33–62 **Vulnerability Type**: Unvalidated destination URL for sensitive network requests **Risk Level**: High ### Vulnerable Code ```python def get_token(api_url, ak, sk, type_value): """获取访问令牌""" token_url = f"{api_url}/api/v2/agent/common/cdk/getToken" secret_string = get_md5(ak + sk) payload = { "akString": ak, "secretString": secret_string, "type": int(type_value), "forceUpdate": 0 } try: response = requests.post(token_url, json=payload) result = response.json() if result.get("code") == "200": return result["data"] else: return None, result.get("message", "获取Token失败") except Exception as e: return None, str(e) ``` ```python def discern_invoice(file_path, tax_no=None): """识别发票""" api_url = os.getenv("HSY_API_URL", "https://huisuiyun.com") ak = os.getenv("HSY_AK") sk = os.getenv("HSY_SK") type_value = os.getenv("HSY_TYPE", "2") # ... headers = {"X-Access-Token": token} if type_value == "1" and tax_no: headers["X-Tax-Token"] = tax_no discern_url = f"{api_url}/api/v2/agent/cdk/invoice/discern" try: with open(file_path, 'rb') as f: files = {'file': f} response = requests.post(discern_url, files=files, headers=headers) return response.json() except Exception as e: return {"error": str(e)} ``` ### Technical Analysis The destination for both authentication and invoice-upload requests is controlled through the `HSY_API_URL` environment variable. The implementation concatenates API paths onto this value without validating its scheme, hostname, port, user-information component, or final destination. Consequently, the value may point to an arbitrary HTTP or HTTPS ...[truncated 3298 chars]
- Remediation
- ## Remediation Suggestions 1. **Pin the production API origin.** Use a constant such as `https://huisuiyun.com` rather than accepting an unrestricted environment-provided URL. ```python API_ORIGIN = "https://huisuiyun.com" ``` 2. **If endpoint configurability is operationally necessary, enforce an explicit allowlist.** Parse the URL and require: - The `https` scheme. - An exact approved hostname. - No username or password component. - No fragments. - Only an approved port, normally `443`. ```python from urllib.parse import urlparse ALLOWED_HOSTS = {"huisuiyun.com"} def validate_api_url(value): parsed = urlparse(value) if ( parsed.scheme != "https" or parsed.hostname not in ALLOWED_HOSTS or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) or parsed.fragment ): raise ValueError("Unapproved HSY API endpoint") return f"https://{parsed.hostname}" ``` 3. **Do not silently permit development endpoints in production.** If testing against alternate servers is required, place that capability behind an explicit development-only option and use a separate set of non-production credentials. 4. **Set connection and response timeouts** on both requests to limit denial-of-service exposure: ```python response = requests.post(token_url, json=payload, timeout=(5, 30)) response.raise_for_status() ``` 5. **Validate authentication responses strictly.** Confirm the HTTP status, response content type, expected JSON structure, and token type before using the returned value. 6. **Avoid redirecting sensitive requests to unapproved origins.** Disable redirects or validate every redirect destination before forwarding credentials or invoice data: ```python requests.post(..., allow_redirects=False) ``` 7. ...[truncated 555 chars]
