🎭
【PHP】CIDRに特定IPアドレスが含まれているかをチェックする
ポイント・結論
- dotted decimal notationからの変換は、
ip2long()
という神関数が用意されている - CIDRにおけるホスト部の桁数をマスクへ変換するには、ビットシフト演算を活用する
- CIDRに特定IPアドレスが含まれているかのチェックは、マスクとの論理積が一致しているかで判定できる
PHP: ip2long - Manual
PHP: Bitwise Operators - Manual
実装
CIDR.php
<?php
class CIDR
{
private int $mask;
private int $binary_network_prefix;
/**
* CIDR constructor.
*
* @param string $cidr 例: 127.0.0.0/30
* @throws Exception
*/
public function __construct(string $cidr)
{
list($network_prefix, $bits) = explode('/', $cidr);
// 例: $bits = 25なら、11111111111111111111111110000000となる
$mask = (0xffffffff << (32 - $bits)) & 0xffffffff;
$binary_network_prefix = ip2long($network_prefix);
if (($binary_network_prefix & $mask) !== $binary_network_prefix) {
// 127.0.0.1/5 のようにCIDR表記として間違っている
throw new Exception('invalid CIDR input.');
}
$this->mask = $mask;
$this->binary_network_prefix = $binary_network_prefix;
}
/**
* @param string $ip 例: 127.0.0.12
* @return bool
*/
public function contains(string $ip): bool
{
// ネットワーク部が一致しているかを判定する
return (ip2long($ip) & $this->mask) === $this->binary_network_prefix;
}
}
$cidr = new CIDR('127.0.0.0/26');
// bool(true) CIDR範囲内
var_dump($cidr->contains('127.0.0.3'));
// bool(false) CIDR範囲外
var_dump($cidr->contains('127.0.0.255'));
// bool(true) ネットワークアドレスも含む
var_dump($cidr->contains('127.0.0.0'));
// bool(true) ブロードキャストアドレスも含む
var_dump($cidr->contains('127.0.0.63'));
// bool(false) ブロードキャストアドレス超過
var_dump($cidr->contains('127.0.0.64'));
注意点
IPアドレスの制限などをアプリケーションのレイヤーで行うのは本当はNG。
基本的にはnginxとかで設定しましょう。
Discussion