T09 · Insecure Skill Coding Practices
Error
- Location
- references/architecture.md:575
- Finding
- Incomplete SSRF Address Validation in Recommended Security Pattern<![CDATA[ ## Vulnerability Details **File Location**: `references/architecture.md`, lines 575–589 **Vulnerability Type**: Server-Side Request Forgery (SSRF) due to incomplete IP-address validation **Risk Level**: High ### Vulnerable Code ```ruby def fetch_safely(url) uri = URI.parse(url) ip = Resolv.getaddress(uri.host) # Block private networks raise "Private IP" if private_ip?(ip) # Use pinned IP for request Net::HTTP.start(uri.host, uri.port, ipaddr: ip) { |http| ... } end def private_ip?(ip) ip.start_with?("127.", "10.", "192.168.") || ip.match?(/^172\.(1[6-9]|2[0-9]|3[0-1])\./) end ``` ### Technical Analysis The Skill presents this code as an SSRF protection pattern, but its address validation is an incomplete textual blocklist. It rejects only IPv4 loopback beginning with `127.`, the `10.0.0.0/8` range, addresses beginning with `192.168.`, and `172.16.0.0/12`. It does not reject several non-public destinations, including: - IPv4 link-local addresses such as `169.254.0.0/16`, including common cloud metadata endpoints. - IPv6 loopback, unique-local, and link-local ranges. - IPv4-mapped IPv6 representations. - Unspecified, multicast, carrier-grade NAT, and other reserved ranges. - Redirect destinations that may resolve to prohibited addresses. The example also does not explicitly restrict schemes, ports, redirect behavior, response size, or network timeouts. DNS pinning reduces DNS-rebinding exposure for the initial resolution, but it does not compensate for the incomplete destination policy. Because this is documentation rather than executable project code, exploitation requires a downstream application to adopt this example and pass attacker-controlled URLs to it. ### Attack Path 1. A developer copies the recommended `fetch_safely` pattern into a Rails application. 2. The application exposes functionality that accepts an attacker-controlled URL, such as URL previews, imports, webhook verification, or remote image retrieval. 3. ...[truncated 1419 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Replace the textual blocklist with a strict outbound-request policy: 1. Parse resolved addresses using Ruby's `IPAddr` rather than string prefixes. 2. Permit only explicitly supported schemes, normally `http` and `https`. 3. Reject every address that is not approved global unicast, covering IPv4 and IPv6 loopback, private, link-local, unique-local, unspecified, multicast, reserved, carrier-grade NAT, and IPv4-mapped IPv6 forms. 4. Prefer an explicit hostname or destination allowlist where the business workflow permits one. 5. Restrict destination ports to the minimum required set. 6. Resolve the hostname once and connect to the validated address while preserving the original hostname for TLS certificate and Host-header validation. 7. Disable redirects by default. If redirects are necessary, apply the complete scheme, port, hostname, DNS, and IP validation process independently to every redirect target. 8. Configure connection, read, and total request timeouts. 9. Limit response sizes and avoid automatically processing untrusted response formats. 10. Route outbound requests through a controlled egress proxy or firewall that blocks metadata, loopback, link-local, private, and reserved destinations. 11. Add regression tests for IPv4 and IPv6 edge cases, including `169.254.169.254`, IPv6 loopback, unique-local addresses, link-local addresses, IPv4-mapped IPv6 addresses, alternate textual representations, and redirect chains. 12. Document that this protection is required only when the application intentionally retrieves remote resources; generated code should not make outbound requests unless necessary for the requested feature. ]]>
