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
pull/657/head
JasonOA888 2 months ago
parent 744d45d2c5
commit 1b97112035

@ -31,6 +31,18 @@ module DownloadUtils
'ip6-allrouters' 'ip6-allrouters'
].freeze ].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) UnableToDownload = Class.new(StandardError)
module_function module_function
@ -55,6 +67,24 @@ module DownloadUtils
raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https' || raise UnableToDownload, "Error loading: #{uri}. Only HTTPS is allowed." if uri.scheme != 'https' ||
[443, nil].exclude?(uri.port) [443, nil].exclude?(uri.port)
raise UnableToDownload, "Error loading: #{uri}. Can't download from localhost." if uri.host.in?(LOCALHOSTS) 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 end
def conn(validate: Docuseal.multitenant?) def conn(validate: Docuseal.multitenant?)

Loading…
Cancel
Save