Dealing with IP range in PHP/MySQL

Published the Wednesday, February 21, 2018 at 7:44 PM Keywords: Web service CIDR Amazon PHP MySQL Azure Microsoft

I recently had to deal with CIDR blocks to tighten some webservice security.

First let's see how to compute the first and last address of an IP range with just the block base IP and mask:

$range = array('127.0.0.1', 8);
function rangeBegin($range) {
	return $range[0];
}
function rangeEnd($range) {
	return long2ip(ip2long($range[0]) | ((1 << (32 - $range[1])) - 1));
}

How to detect if an IP is present in a CIDR block:

$ip = '127.0.0.1';
$range = array('127.0.0.1', 8);
function ipInRange($ip, $range) {
	if (ip2long($range[0]) <= ip2long($ip) && ip2long($ip) <= (ip2long($range[0]) | ((1 << (32 - $range[1])) - 1))) {
		return true;
	}
	return false;
}

As a first bonus, how to retrieve amazon IP ranges:

function fetchAmazonRange() {
	//Init array
	$amazonRanges = array();

	$ctx = stream_context_create(
		array(
			'http' => array(
				'method' => 'GET',
				'max_redirects' => 0,
				'timeout' => 5,
				'ignore_errors' => false,
				'header' => array(
					'Connection: close',
					'Accept: application/json'
				)
			)
		)
	);

	//Fetch json
	if (($json = file_get_contents('https://ip-ranges.amazonaws.com/ip-ranges.json', false, $ctx)) === false) {
		return null;
	}

	//Decode it
	if (($json = json_decode($json)) === null || empty($json->prefixes)) {
		return false;
	}

	//Deal with prefixes
	foreach($json->prefixes as $range) {
		//Skip ipv6 and invalid ranges
		if (empty($range->ip_prefix)||!preg_match('/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\/([0-9]+)$/', $range->ip_prefix, $matche
s)) {
			continue;
		}
		//Remove whole match
		array_shift($matches);
		//Add ip and mask
		$amazonRanges[] = $matches;
	}

	//Send back result
	return $amazonRanges;
}

Microsoft Azure Ip ranges urls: