Validating non-private IP addresses with PHP
Asked Answered
J

3

10

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.

Jo answered 17/6, 2013 at 14:33 Comment(2)
127.0.0.1 is not in a private range, it is the loopback IP. The doc clearly states that FILTER_FLAG_NO_PRIV_RANGE only rejects 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.Hock
For others seeing this, you can use the FILTER_FLAG_NO_RES_RANGE to check for 127.0.0.1. As so filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);Riki
B
6

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
?>
Bunko answered 17/6, 2013 at 14:42 Comment(0)
K
2

It's now blocked by FILTER_FLAG_NO_RES_RANGE.

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

Karajan answered 13/11, 2015 at 20:4 Comment(0)
K
0

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.

Keenan answered 12/6, 2023 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.