Skip to content

Add reserved? method to detect reserved IP addresses #80

Open

Description

@collimarco

This library already has private?, link_local?, loopback?, etc.

It would be useful to have a reserved? method:
https://en.wikipedia.org/wiki/Reserved_IP_addresses

One of the reasons would be to mitigate SSRF attacks in a simple way, by limiting connections to "normal" "external" IP addresses (!ipaddr.reserved?).

Activity

  1. collimarco commented on Nov 4, 2024

    @collimarco
    Author

    Elixir already has a module for this: https://hexdocs.pm/safeurl/SafeURL.html

  2. taketo1113 commented on Apr 24, 2026

    @taketo1113
    Collaborator

    @collimarco

    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?

  3. jonasporto commented on Oct 16, 2026

    @jonasporto

    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:

    1. Name and source of truth: global? meaning "Globally Reachable: True in the IANA registries", with the registry rows hard-coded like the existing predicates?
    2. 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?
    3. For a network (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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions