mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-28 00:16:48 +09:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae7a34113f |
No files matched your search
@@ -8,6 +8,14 @@ module RestrictedHTTP
|
|||||||
|
|
||||||
LOCAL_IP = IPAddr.new("0.0.0.0/8") # "This" network
|
LOCAL_IP = IPAddr.new("0.0.0.0/8") # "This" network
|
||||||
|
|
||||||
|
# IPv6 transition/encapsulation and shared-address ranges that can smuggle
|
||||||
|
# requests toward internal networks and must never be reachable.
|
||||||
|
DISALLOWED_RANGES = [
|
||||||
|
IPAddr.new("64:ff9b::/96"), # NAT64 (RFC 6052) — embeds IPv4 in IPv6
|
||||||
|
IPAddr.new("2002::/16"), # 6to4 (RFC 3056) — embeds IPv4 in IPv6
|
||||||
|
IPAddr.new("100.64.0.0/10") # CGNAT / shared address space (RFC 6598)
|
||||||
|
]
|
||||||
|
|
||||||
def resolve(hostname)
|
def resolve(hostname)
|
||||||
Resolv.getaddress(hostname).tap do |ip|
|
Resolv.getaddress(hostname).tap do |ip|
|
||||||
raise Violation.new("Attempt to access private IP via #{hostname}") if ip && private_ip?(ip)
|
raise Violation.new("Attempt to access private IP via #{hostname}") if ip && private_ip?(ip)
|
||||||
@@ -16,7 +24,7 @@ module RestrictedHTTP
|
|||||||
|
|
||||||
def private_ip?(ip)
|
def private_ip?(ip)
|
||||||
IPAddr.new(ip).then do |ipaddr|
|
IPAddr.new(ip).then do |ipaddr|
|
||||||
ipaddr.private? || ipaddr.loopback? || ipaddr.link_local? || ipaddr.ipv4_mapped? || ipaddr.ipv4_compat? || LOCAL_IP.include?(ipaddr)
|
ipaddr.private? || ipaddr.loopback? || ipaddr.link_local? || ipaddr.ipv4_mapped? || ipaddr.ipv4_compat? || LOCAL_IP.include?(ipaddr) || DISALLOWED_RANGES.any? { |range| range.include?(ipaddr) }
|
||||||
end
|
end
|
||||||
rescue IPAddr::InvalidAddressError
|
rescue IPAddr::InvalidAddressError
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -64,6 +64,26 @@ class RestrictedHTTP::PrivateNetworkGuardTest < ActiveSupport::TestCase
|
|||||||
assert_private_ip "::93.184.216.34"
|
assert_private_ip "::93.184.216.34"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# IPv6/encapsulation and shared-address ranges (SSRF bypass prevention)
|
||||||
|
|
||||||
|
test "private_ip? returns true for NAT64 addresses" do
|
||||||
|
assert_private_ip "64:ff9b::a00:1" # NAT64-embedded 10.0.0.1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "private_ip? returns true for 6to4 addresses" do
|
||||||
|
assert_private_ip "2002::1"
|
||||||
|
assert_private_ip "2002:0a00:0001::" # 6to4-embedded 10.0.0.1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "private_ip? returns true for CGNAT / shared address space" do
|
||||||
|
assert_private_ip "100.64.0.1"
|
||||||
|
assert_private_ip "100.127.255.255"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "private_ip? returns false for public addresses outside the new ranges" do
|
||||||
|
assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("93.184.216.34")
|
||||||
|
end
|
||||||
|
|
||||||
test "private_ip? returns true for invalid addresses" do
|
test "private_ip? returns true for invalid addresses" do
|
||||||
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("not-an-ip")
|
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("not-an-ip")
|
||||||
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("")
|
assert RestrictedHTTP::PrivateNetworkGuard.private_ip?("")
|
||||||
|
|||||||
Reference in new issue
Block a user