You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add reserved? method to detect reserved IP addresses #80
One of the reasons would be to mitigate SSRF attacks in a simple way, by limiting connections to "normal" "external" IP addresses (!ipaddr.reserved?).
In this use case, it is not possible to make a strict determination based on whether the address is reserved, so it might be better to have a method that determines whether an address is globally reachable. What do you think? #68 (comment)
Also, are there any other real-world use cases where reserved? would apply?
A concrete use case, and a detail that matters for the shape of the method (following @taketo1113's suggestion here and in #68 of a "globally reachable" predicate rather than reserved?).
Use case. A server that fetches a URL a third party chose: an outgoing webhook whose URL a user typed, or an image proxy. Before connecting, it resolves the name, requires every resolved address to be globally reachable, then connects to the checked address (so DNS rebinding changes nothing). Today that needs loopback?, private?, link_local?, multicast? plus a hand-kept list for everything else in the IANA special-purpose registries (IPv4, IPv6). The rows marked "Globally Reachable: False" that no current predicate covers:
Block
Name
RFC
0.0.0.0/8
"This network"
791
100.64.0.0/10
Shared Address Space
6598
192.0.0.0/24
IETF Protocol Assignments (except .9 and .10, which are True)
6890
192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24
Documentation
5737
198.18.0.0/15
Benchmarking
2544
240.0.0.0/4, 255.255.255.255/32
Reserved, Limited Broadcast
1112, 919
::/128
Unspecified
4291
64:ff9b:1::/48
IPv4-IPv6 Translation, local use
8215
100::/64, 100:0:0:1::/64
Discard-Only, Dummy prefix
6666, 9780
2001::/23
IETF Protocol Assignments (with the True exceptions listed in the registry)
2928
2001:db8::/32, 3fff::/20
Documentation
3849, 9637
5f00::/16
SRv6 SIDs
9602
The IPv6 forms that carry an IPv4 address. For the SSRF case the registry's column alone is not enough, because some IPv6 addresses are delivered to the IPv4 address inside them:
Address
Form
Registry says
Reaches
::ffff:10.0.0.1
IPv4-mapped, RFC 4291
False
10.0.0.1
64:ff9b::10.0.0.1
NAT64 well-known prefix, RFC 6052
True
10.0.0.1, through any NAT64 gateway on the path
2002:a00:1::
6to4, RFC 3056
N/A
10.0.0.1
2001:0:4136:e378:8000:63bf:f5ff:fffe
Teredo, RFC 4380 (client field is 10.0.0.1, XORed)
N/A
10.0.0.1
::10.0.0.1
IPv4-compatible, deprecated by RFC 4291, not in the registry
not listed
10.0.0.1 on stacks that still honour it
IPAddr already judges the first form by its embedded IPv4 (IPAddr.new("::ffff:10.0.0.1").private? # => true, since #57/#65); every other row returns false from private?, loopback? and link_local? on 1.3.0 (master a3f7dbe). Python's ipaddress, which follows the registry, shows the trap: on 3.15.0 ip_address("64:ff9b::10.0.0.1").is_global and ip_address("::10.0.0.1").is_global are both True.
So the questions before a PR, if one is welcome:
Name and source of truth: global? meaning "Globally Reachable: True in the IANA registries", with the registry rows hard-coded like the existing predicates?
The rows marked N/A (6to4, Teredo) and the NAT64 well-known prefix: judge them by the embedded IPv4 address, as private? does for IPv4-mapped, or leave them to the registry's answer and document that an SSRF check must look inside them?
Activity
collimarco commentedon Nov 4, 2024
Elixir already has a module for this: https://hexdocs.pm/safeurl/SafeURL.html
taketo1113 commentedon Apr 24, 2026
@collimarco
In this use case, it is not possible to make a strict determination based on whether the address is reserved, so it might be better to have a method that determines whether an address is globally reachable. What do you think?
#68 (comment)
Also, are there any other real-world use cases where
reserved?would apply?jonasporto commentedon Oct 16, 2026
A concrete use case, and a detail that matters for the shape of the method (following @taketo1113's suggestion here and in #68 of a "globally reachable" predicate rather than
reserved?).Use case. A server that fetches a URL a third party chose: an outgoing webhook whose URL a user typed, or an image proxy. Before connecting, it resolves the name, requires every resolved address to be globally reachable, then connects to the checked address (so DNS rebinding changes nothing). Today that needs
loopback?,private?,link_local?,multicast?plus a hand-kept list for everything else in the IANA special-purpose registries (IPv4, IPv6). The rows marked "Globally Reachable: False" that no current predicate covers:0.0.0.0/8100.64.0.0/10192.0.0.0/24.9and.10, which are True)192.0.2.0/24,198.51.100.0/24,203.0.113.0/24198.18.0.0/15240.0.0.0/4,255.255.255.255/32::/12864:ff9b:1::/48100::/64,100:0:0:1::/642001::/232001:db8::/32,3fff::/205f00::/16The IPv6 forms that carry an IPv4 address. For the SSRF case the registry's column alone is not enough, because some IPv6 addresses are delivered to the IPv4 address inside them:
::ffff:10.0.0.164:ff9b::10.0.0.12002:a00:1::2001:0:4136:e378:8000:63bf:f5ff:fffe::10.0.0.1IPAddr already judges the first form by its embedded IPv4 (
IPAddr.new("::ffff:10.0.0.1").private? # => true, since #57/#65); every other row returnsfalsefromprivate?,loopback?andlink_local?on 1.3.0 (master a3f7dbe). Python'sipaddress, which follows the registry, shows the trap: on 3.15.0ip_address("64:ff9b::10.0.0.1").is_globalandip_address("::10.0.0.1").is_globalare bothTrue.So the questions before a PR, if one is welcome:
global?meaning "Globally Reachable: True in the IANA registries", with the registry rows hard-coded like the existing predicates?private?does for IPv4-mapped, or leave them to the registry's answer and document that an SSRF check must look inside them?IPAddr.new("10.0.0.0/8")), the same rule as Fix false positives in private?/link_local?/multicast?/link_local_multicast? for supernets of the target address blocks #125: true only when the whole block qualifies?I can send a PR with tests in the style of the existing predicates once the shape is settled.