PHP: Determining if a server/domain/IP is internal

This application supports FOAF profile import. This was one of the earlier features. It works although I’m not sure how useful it is at the moment. As part of the system, users are asked to enter the URL for a profile in XML format. We take this URL and request it. Before doing that however, we have to check that the URL is on a non-internal publicly accessible server. Without a check, a user could enter a domain on the LAN which could expose data.

Web applications implementing RSS checking, OpenID and other similar technologies should perform these checks. First, a bit of background information.

IP Addresses and Long Addresses

Every IP address has an equivalent long address. IP addresses are in the form of 0-255.0-255.0-255.0-255 giving a total of 4,294,967,296 possible combinations. A long IP address is a number between 0 and 4,294,967,295. Some of you may notice that this is also the 32-bit unsigned integer limit. In other words, any IP address can also be represented as an 32-bit integer. An address such as 127.0.0.1 is much easier to remember than something like 2130706433 so we tend to use them.

It is actually possible to use the long IP address format. If you don’t believe me, try it. If you’ve got a web server installed on your local machine and it’s turned on, try going to http://2130706433/. If you don’t, try Google (1113983336). This doesn’t work with all ISPs although the long IP address for localhost should always work. Try pinging 2130706433 from the command line.

Internal IP Address Ranges

There are several blocks reserved for internal IP addressing. These are 10.0.0.0 to 10.255.255.255, 172.16.0.0 to 172.31.255.255 and 192.168.0.0 to 192.168.255.255. In addition the block 169.254.0.0 to 169.254.255.255 is reserved for automatic private IP addressing and 127.0.0.0 to 127.255.255.255 is only really ever used for loopback so it makes sense to block access to these IPs.

In order to detect whether an IP is internal or publicly accessible, we need to check if the IP address is in this range. It’s possible to use regexps or other string matching techniques but this is more difficult. Instead, we convert the IP address from the dotted format to the long format. Luckily PHP provides us with a built in function to do this.

We need to generate a long format IP for the beginning and end IP for each range and then check whether the IP address are in those ranges by using greater than/lesser than comparison operators.

Here’s some code I cooked up earlier:


<?php

function urlIsPublic($url) {

        
/* Check URL is not private */

        
$parsedurl = parse_url($url);

        
$ip = gethostbyname($parsedurl['host']);

        $long = ip2long($ip);

        if ((
$long >= 167772160 AND $long <= 184549375) OR ($long >= -1408237568 AND $long <= -1407188993) OR ($long >= -1062731776 AND $long <= -1062666241) OR ($long >= 2130706432 AND $long <= 2147483647) OR $long == -1) {

            return false;

        }

        return true;

        // 167772160 - 10.0.0.0

        // 184549375 - 10.255.255.255

        //

        // -1408237568 - 172.16.0.0

        // -1407188993 - 172.31.255.255

        //

        // -1062731776 - 192.168.0.0

        // -1062666241 - 192.168.255.255

        //

        // -1442971648 - 169.254.0.0

        // -1442906113 - 169.254.255.255

        //

        // 2130706432 - 127.0.0.0

        // 2147483647 - 127.255.255.255 (32 bit integer limit!!!)

        //

        // -1 is also b0rked

}

?>

Notice in the code I’ve also checked for ip2long() responding with a -1. This indicates an invalid IP address.

The first few lines take a URL and extracts the domain from it. It then converts the domain name into an IP address for use with the checker.

2 thoughts on “PHP: Determining if a server/domain/IP is internal

  1. thx about info….

    about longip, i can ping it, but cant open it at my browser. i’ve disconnect my connection with internet(i use dial up to connect) and i cant open it again, my web server is up of course.

    i use windows XP SP2, with Apache 2.0.2, and i use phone modem to dial up. there’s a problem with my computer?

     i want to ask this "old fashion" question via email, but i looked everywhere in the blog, and can’t find it. if you want to reply, please reply to my email please.

  2. This method works but is not FULLY cooked. This will only work for 32 bit systems. If you push this code to a 64bit system you will find that it no longer works. You just have to check to see if the system is 32 or 64bit and use a different if statement when checking the values.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>