はじめに
Amazon CloudFrontを使ってアクセスした際は接続元IPアドレスが「X-Forwarded-For」ヘッダに格納されます。
このヘッダには複数IPアドレスが列挙されることがあり、先頭からひとつずつ検証する必要があります。
このクラスは「CloudFront IPアドレスに一致するか」をAWS IPアドレスの範囲に基づいて検証しています。
HTTP接続用にGuzzleを使っていますので、必要に応じてインストールしてください。
検証用クラス
<?php
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
class AmazonAddress
{
const AMAZON_IP_RANGES_URL = "https://ip-ranges.amazonaws.com/ip-ranges.json";
/**
* 対象IPv4アドレスがAWS CloudFrontのIPv4かを判定する
*
* @param string $remote_ip 対象IPv4アドレス
* @return bool 判定結果(true=CloudFront)
*/
public static function isCloudFrontIPv4Address($remote_ip)
{
$prefixes = static::getAwsIPv4Addresses('CLOUDFRONT');
foreach ($prefixes as $prefix) {
list($accept_ip, $mask) = explode('/', $prefix);
$accept_long = ip2long($accept_ip) >> (32 - $mask);
$remote_long = ip2long($remote_ip) >> (32 - $mask);
if ($accept_long == $remote_long) {
return true;
}
}
return false;
}
/**
* AWS IPv4レンジリストを返却する
* リクエストコストを考慮する必要がある場合は、リクエスト結果キャッシュなど対策すること
* サービス名は以下を参照:
* https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-ip-ranges.html
*
* @param string $service_name サービス名
* @return array IPv4レンジリスト
*/
public static function getAwsIPv4Addresses($service_name)
{
$ip_prefixes = [];
$client = new Client();
$response = $client->get(
static::AMAZON_IP_RANGES_URL,
[
RequestOptions::ALLOW_REDIRECTS => false,
RequestOptions::TIMEOUT => 10,
RequestOptions::CONNECT_TIMEOUT => 1
]
);
// 200以外が返却された場合は空配列を返却する
if ($response->getStatusCode() != 200) {
return $ip_prefixes;
}
$response_body = (string)$response->getBody();
$response_json = json_decode($response_body);
// JSONパースに失敗した場合は空配列を返却する
if (json_last_error() !== JSON_ERROR_NONE) {
return $ip_prefixes;
}
foreach ($response_json->prefixes as $item) {
// 対象サービスを限定
if ($item->service != $service_name) {
continue;
}
$ip_prefixes[] = $item->ip_prefix;
}
return $ip_prefixes;
}
}
使い方
AmazonAddress::isCloudFrontIPv4Address('52.46.54.119') // → true
AmazonAddress::isCloudFrontIPv4Address('1.1.1.1') // → false