T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/shopify_bulk_upload.py:104
- Finding
- Unrestricted Image URLs Enable Blind Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shopify_bulk_upload.py:104-106`, with attacker-controlled input reaching the vulnerable method at `scripts/shopify_bulk_upload.py:200-205` **Vulnerability Type**: Blind server-side request forgery (SSRF) **Risk Level**: Medium ### Vulnerable Code ```python def upload_image(self, product_id, image_url): """上传产品图片""" try: # 下载图片 img_response = requests.get(image_url, timeout=CONFIG["image_timeout"]) if img_response.status_code != 200: logger.warning(f"无法下载图片: {image_url}") return None ``` The image URL originates from product-file input: ```python # 上传图片 images = product_data.get("images", "") if images: image_urls = [img.strip() for img in images.split(",")] for img_url in image_urls: if img_url: self.upload_image(product_id, img_url) ``` ### Technical Analysis The `images` field is read from an operator-supplied CSV or Excel file and passed directly to `requests.get()`. The code does not validate: - URL scheme - Destination hostname - Resolved IP address - Redirect targets - Destination port - Loopback, private, link-local, or reserved address ranges - Cloud instance metadata endpoints Consequently, a malicious product file can cause the uploader host to issue HTTP requests to network resources that are not accessible to the attacker directly. The local image download is also unnecessary for the declared workflow. The downloaded bytes are not included in the Shopify request; the method later submits only the original URL as Shopify's `image.src`. This unnecessary request exceeds the minimum network access required for Shopify image import. The current implementation does not return the downloaded response body to the input provider, so the issue is blind SSRF rather than direct response exfiltration. Nevertheless, timing, status-dependent behavior, and application logs may reveal whether a destination is re ...[truncated 1466 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Remove the local download entirely.** Shopify is already instructed to retrieve the image through the submitted `src` URL, so the local `requests.get()` call and unused byte encoding should be deleted. 2. If local validation is a functional requirement, implement a strict URL policy: - Permit only `https` URLs. - Reject embedded credentials, fragments, malformed hosts, and unexpected ports. - Resolve the hostname before connecting. - Reject loopback, private, link-local, multicast, unspecified, reserved, and metadata IP ranges for both IPv4 and IPv6. - Revalidate every redirect destination or disable redirects. - Protect against DNS rebinding by connecting only to the validated resolved address. - Apply a maximum response size and validate the response content type. - Use short connection and read timeouts. 3. Consider an explicit domain allowlist if product images are expected to come from known content-delivery or supplier domains. 4. Log rejected destinations without downloading them, and avoid placing sensitive URL components in logs. ]]>
