Module: Iron::SsrfProtection
- Extended by:
- SsrfProtection
- Included in:
- SsrfProtection
- Defined in:
- app/models/iron/ssrf_protection.rb
Constant Summary collapse
- DNS_RESOLUTION_TIMEOUT =
2- GLOBAL_UNICAST_V6 =
2000::/3 is the only IPv6 block IANA has delegated as global unicast, so anything outside it (unspecified, loopback, link-local, ULA, multicast, translation, discard) is non-global by construction. Within it, a few sub-ranges are still reserved and must be denied explicitly.
IPAddr.new("2000::/3")
- DISALLOWED_RANGES =
[ IPAddr.new("0.0.0.0/8"), # "This" network / unspecified (RFC1122) IPAddr.new("100.64.0.0/10"), # Carrier-grade NAT (RFC6598) IPAddr.new("192.0.0.0/24"), # IETF protocol assignments (RFC6890) IPAddr.new("192.0.2.0/24"), # Documentation TEST-NET-1 (RFC5737) IPAddr.new("198.18.0.0/15"), # Benchmark testing (RFC2544) IPAddr.new("198.51.100.0/24"), # Documentation TEST-NET-2 (RFC5737) IPAddr.new("203.0.113.0/24"), # Documentation TEST-NET-3 (RFC5737) IPAddr.new("192.88.99.0/24"), # 6to4 relay anycast (RFC7526) IPAddr.new("224.0.0.0/4"), # IPv4 multicast (RFC5771) IPAddr.new("240.0.0.0/4"), # Reserved (RFC1112) IPAddr.new("2001::/23"), # IETF protocol assignments — non-global (Teredo, ORCHID, benchmarking) IPAddr.new("2001:db8::/32"), # Documentation (RFC3849) IPAddr.new("2002::/16"), # 6to4 (RFC3056) — non-global IPAddr.new("3fff::/20") # Documentation (RFC9637) ].freeze
Instance Method Summary collapse
- #blocked?(ip) ⇒ Boolean
-
#public_addresses(hostname) ⇒ Object
Resolves a hostname and returns its addresses only when every one of them is public, so a caller can connect to a vetted IP without re-resolving.
- #public_host?(hostname) ⇒ Boolean
Instance Method Details
#blocked?(ip) ⇒ Boolean
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'app/models/iron/ssrf_protection.rb', line 46 def blocked?(ip) ip = IPAddr.new(ip.to_s) unless ip.is_a?(IPAddr) return true if ip.ipv6? && !GLOBAL_UNICAST_V6.include?(ip) ip.private? || ip.loopback? || ip.link_local? || ip.ipv4_mapped? || ip.ipv4_compat? || DISALLOWED_RANGES.any? { |range| range.include?(ip) } rescue IPAddr::Error true end |
#public_addresses(hostname) ⇒ Object
Resolves a hostname and returns its addresses only when every one of them is public, so a caller can connect to a vetted IP without re-resolving.
39 40 41 42 43 44 |
# File 'app/models/iron/ssrf_protection.rb', line 39 def public_addresses(hostname) addresses = resolve(hostname) return [] if addresses.empty? || addresses.any? { |ip| blocked?(ip) } addresses.map(&:to_s) end |
#public_host?(hostname) ⇒ Boolean
33 34 35 |
# File 'app/models/iron/ssrf_protection.rb', line 33 def public_host?(hostname) public_addresses(hostname).any? end |