From 1b9711203573b4b45aa07df9a335ab9636bbfc4f Mon Sep 17 00:00:00 2001 From: JasonOA888 Date: Wed, 6 May 2026 02:44:25 +0800 Subject: [PATCH] Harden SSRF protection in DownloadUtils The existing check only blocks known localhost hostnames. This adds DNS resolution and private IP range blocking to prevent SSRF via: - Domains resolving to private IPs (192.168.x.x, 10.x.x.x, etc.) - Access to cloud metadata endpoints (169.254.169.254) - IPv6 link-local and unique-local addresses --- lib/download_utils.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/download_utils.rb b/lib/download_utils.rb index 8b352502..7d4660ae 100644 --- a/lib/download_utils.rb +++ b/lib/download_utils.rb @@ -31,6 +31,18 @@ module DownloadUtils 'ip6-allrouters' ].freeze + BLOCKED_CIDRS = [ + IPAddr.new('10.0.0.0/8'), + IPAddr.new('172.16.0.0/12'), + IPAddr.new('192.168.0.0/16'), + IPAddr.new('127.0.0.0/8'), + IPAddr.new('169.254.0.0/16'), + IPAddr.new('100.64.0.0/10'), + IPAddr.new('::1/128'), + IPAddr.new('fc00::/7'), + IPAddr.new('fe80::/10') + ].freeze + UnableToDownload = Class.new(StandardError) module_function @@ -55,6 +67,24 @@ module DownloadUtils raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https' || [443, nil].exclude?(uri.port) raise UnableToDownload, "Error loading: #{uri}. Can't download from localhost." if uri.host.in?(LOCALHOSTS) + + validate_resolved_ip!(uri.host) + end + + def validate_resolved_ip!(host) + addresses = Resolv.getaddresses(host) + + addresses.each do |addr| + ip = begin + IPAddr.new(addr) + rescue IPAddr::InvalidAddressError + next + end + + if BLOCKED_CIDRS.any? { |cidr| cidr.include?(ip) } + raise UnableToDownload, "Can't download from private/reserved IP: #{addr}" + end + end end def conn(validate: Docuseal.multitenant?)