T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/synthesize.py:364
- Finding
- Unvalidated API-Provided Audio URL Enables Server-Side Request Forgery## Vulnerability Details **File Location**: `scripts/synthesize.py:364-381`, with the untrusted URL extracted and passed to the download function at `scripts/synthesize.py:590-611` **Vulnerability Type**: Server-Side Request Forgery through an unvalidated remote URL **Risk Level**: Medium ### Vulnerable Code ```python def download_audio(url: str, output_path: str) -> bool: """ 下载音频文件 Args: url: 音频文件 URL output_path: 保存路径 Returns: 是否下载成功 """ try: response = requests.get(url, timeout=120, stream=True) response.raise_for_status() with open(output_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: f.write(chunk) return True ``` The URL is obtained from the remote API response and passed directly to the vulnerable function: ```python audio_info = output.get("audio", {}) audio_url = audio_info.get("url") audio_id = audio_info.get("id") expires_at = audio_info.get("expires_at") ``` ```python if args.no_download: print(f"音频 URL: {audio_url}") else: print(f"正在下载音频到: {args.output}") if download_audio(audio_url, args.output): # 获取文件大小 file_size = os.path.getsize(args.output) ``` ### Technical Analysis The application treats `output.audio.url` from the DashScope API response as a trusted download location. It performs a network request with `requests.get()` without validating: - The URL scheme - The destination hostname - The resolved IP address - Whether the destination is loopback, link-local, private, reserved, or otherwise non-public - Redirect destinations - The response content type - The maximum permitted response size The `requests` library follows redirects by default. Consequently, validating only the initial URL would not be sufficient unless every redirect target were also checked. Although `urlparse` is imported ...[truncated 1928 chars]
- Remediation
- ## Remediation Suggestions 1. **Require a valid HTTPS URL** - Reject missing values, non-string values, embedded credentials, fragments, and every scheme other than `https`. - Parse the URL with `urllib.parse.urlparse()` before making a request. 2. **Allowlist trusted download hosts** - Permit only the documented Alibaba Cloud or DashScope audio-storage domains. - Compare normalized hostnames exactly or against carefully defined subdomain boundaries; do not use substring matching. 3. **Block non-public destinations** - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, unspecified, and non-global IPv4 and IPv6 addresses. - Account for all returned addresses and DNS rebinding risks. 4. **Secure redirect handling** - Prefer `allow_redirects=False`. - If redirects are necessary, apply the complete scheme, hostname, and resolved-address validation process to every redirect target and enforce a small redirect limit. 5. **Constrain downloaded responses** - Verify that the response content type is an expected audio media type. - Enforce a strict maximum `Content-Length` and independently count streamed bytes to handle absent or false headers. - Abort and remove partial files when the limit is exceeded. 6. **Use safe file replacement** - Download to a temporary file in the intended destination directory. - Validate successful completion before atomically replacing the target. - Remove temporary or partial files on every failure path. 7. **Validate response structure** - Ensure `output.audio.url` exists and is a non-empty string before attempting the download. - Treat malformed API responses as errors rather than forwarding values directly to the network client.
