Module: FinalDestination::SSRFDetector
- Defined in:
- lib/final_destination/ssrf_detector.rb
Defined Under Namespace
Classes: DisallowedIpError, LookupFailedError
Constant Summary
collapse
- PRIVATE_IPV4_RANGES =
[
IPAddr.new("0.0.0.0/8"),
IPAddr.new("10.0.0.0/8"),
IPAddr.new("100.64.0.0/10"),
IPAddr.new("127.0.0.0/8"),
IPAddr.new("169.254.0.0/16"),
IPAddr.new("172.16.0.0/12"),
IPAddr.new("192.0.0.0/24"),
IPAddr.new("192.0.0.0/29"),
IPAddr.new("192.0.0.8/32"),
IPAddr.new("192.0.0.170/32"),
IPAddr.new("192.0.0.171/32"),
IPAddr.new("192.0.2.0/24"),
IPAddr.new("192.168.0.0/16"),
IPAddr.new("192.175.48.0/24"),
IPAddr.new("198.18.0.0/15"),
IPAddr.new("198.51.100.0/24"),
IPAddr.new("203.0.113.0/24"),
IPAddr.new("240.0.0.0/4"),
IPAddr.new("255.255.255.255/32"),
]
- PRIVATE_IPV6_RANGES =
[
IPAddr.new("::1/128"),
IPAddr.new("::/128"),
IPAddr.new("64:ff9b:1::/48"),
IPAddr.new("100::/64"),
IPAddr.new("2001::/23"),
IPAddr.new("2001:2::/48"),
IPAddr.new("2001:db8::/32"),
IPAddr.new("fc00::/7"),
IPAddr.new("fe80::/10"),
]
- PRIVATE_IP_RANGES =
PRIVATE_IPV4_RANGES + PRIVATE_IPV6_RANGES
Class Method Summary
collapse
Class Method Details
.allow_ip_lookups_in_test! ⇒ Object
109
110
111
|
# File 'lib/final_destination/ssrf_detector.rb', line 109
def self.allow_ip_lookups_in_test!
@allow_ip_lookups_in_test = true
end
|
.allowed_internal_hosts ⇒ Object
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# File 'lib/final_destination/ssrf_detector.rb', line 63
def self.allowed_internal_hosts
hosts =
[
SiteSetting.Upload.s3_cdn_url,
GlobalSetting.try(:cdn_url),
Discourse.base_url_no_prefix,
].filter_map do |url|
URI.parse(url).hostname if url
rescue URI::Error
nil
end
hosts += SiteSetting.allowed_internal_hosts.split(/[|\n]/).filter_map { |h| h.strip.presence }
hosts
end
|
.blocked_ip_blocks ⇒ Object
52
53
54
55
56
57
58
59
60
61
|
# File 'lib/final_destination/ssrf_detector.rb', line 52
def self.blocked_ip_blocks
SiteSetting
.blocked_ip_blocks
.split(/[|\n]/)
.filter_map do |r|
IPAddr.new(r.strip)
rescue IPAddr::InvalidAddressError
nil
end
end
|
.disallow_ip_lookups_in_test! ⇒ Object
113
114
115
|
# File 'lib/final_destination/ssrf_detector.rb', line 113
def self.disallow_ip_lookups_in_test!
@allow_ip_lookups_in_test = false
end
|
.host_bypasses_checks?(hostname) ⇒ Boolean
80
81
82
|
# File 'lib/final_destination/ssrf_detector.rb', line 80
def self.host_bypasses_checks?(hostname)
allowed_internal_hosts.any? { |h| h.downcase == hostname.downcase }
end
|
.ip_allowed?(ip) ⇒ Boolean
84
85
86
87
88
89
90
91
|
# File 'lib/final_destination/ssrf_detector.rb', line 84
def self.ip_allowed?(ip)
ip = ip.is_a?(IPAddr) ? ip : IPAddr.new(ip)
ip = ip.native
return false if ip_in_ranges?(ip, blocked_ip_blocks) || ip_in_ranges?(ip, PRIVATE_IP_RANGES)
true
end
|
.lookup_and_filter_ips(name, timeout: nil) ⇒ Object
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
# File 'lib/final_destination/ssrf_detector.rb', line 93
def self.lookup_and_filter_ips(name, timeout: nil)
begin
ips = lookup_ips(name, timeout: timeout)
rescue SocketError
raise LookupFailedError, "FinalDestination: lookup failed"
end
return ips if host_bypasses_checks?(name)
ips.filter! { |ip| FinalDestination::SSRFDetector.ip_allowed?(ip) }
raise DisallowedIpError, "FinalDestination: all resolved IPs were disallowed" if ips.empty?
ips
end
|