Tim Fountain
Tim Fountain

Reputation: 33148

Validating non-private IP addresses with PHP

I'm trying to check whether or not an IP address is an internal-only (i.e. private) IP, but I'm getting a curious result:

filter_var('173.194.66.94', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns 173.194.66.94
filter_var('192.168.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns false
filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns 127.0.0.1?

Surely 127.0.0.1 counts as a private IP? I found this bug report from 2010 which reports this as an issue, but it's marked as fixed. Is this a regression, or am I misunderstanding what this filter does? I'm using PHP 5.4.6.

Upvotes: 10

Views: 6405

Answers (3)

Lexib0y
Lexib0y

Reputation: 500

As of PHP version 8.2 you can use the new option "FILTER_FLAG_GLOBAL_RANGE", see the description below:

Added the FILTER_FLAG_GLOBAL_RANGE option when validating with the FILTER_VALIDATE_IP flag in the filter functions. The filter will fail when non-global IPV4/IPV6 IP ranges are used in accordance with RFC 6890 where the Global attribute is false.

Upvotes: 0

Ian Dunn
Ian Dunn

Reputation: 3680

It's now blocked by FILTER_FLAG_NO_RES_RANGE.

See https://bugs.php.net/bug.php?id=53150

Upvotes: 2

Hugo Delsing
Hugo Delsing

Reputation: 14173

I guess thats because 127.0.0.1 is not realy a private IP range, but a loopback IP range, as explained here

Normally, when a TCP/IP application wants to send information, that information travels down the protocol layers to IP where it is encapsulated in an IP datagram. That datagram then passes down to the data link layer of the device's physical network for transmission to the next hop, on the way to the IP destination.

However, one special range of addresses is set aside for loopback functionality. This is the range 127.0.0.0 to 127.255.255.255. IP datagrams sent by a host to a 127.x.x.x loopback address are not passed down to the data link layer for transmission. Instead, they “loop back” to the source device at the IP level. In essence, this represents a “short-circuiting” of the normal protocol stack; data is sent by a device's layer three IP implementation and then immediately received by it.

The purpose of the loopback range is testing of the TCP/IP protocol implementation on a host. Since the lower layers are short-circuited, sending to a loopback address allows the higher layers (IP and above) to be effectively tested without the chance of problems at the lower layers manifesting themselves. 127.0.0.1 is the address most commonly used for testing purposes.

The manual for Filter flag has a comment on this specific issue.

<?php
function FILTER_FLAG_NO_LOOPBACK_RANGE($value) {
    // Fails validation for the following loopback IPv4 range: 127.0.0.0/8
    // This flag does not apply to IPv6 addresses
    return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $value :
        (((ip2long($value) & 0xff000000) == 0x7f000000) ? FALSE : $value);
}

$var = filter_var('127.0.0.1', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns FALSE

$var = filter_var('74.125.19.103', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns '74.125.19.103'

// To filter Private IP ranges and Loopback ranges
$var = filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)  && filter_var('127.0.0.1', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns FALSE
?>

Upvotes: 6

Related Questions