はじめに
2026年10月21日、PHPの重大な脆弱性 PHP2Shell(CVE-2026-109618 / CVE-2026-109619)が公開されました。CVSSv4.0スコアは9.2。PHP 5.4以降のほぼすべてのバージョンに影響し、認証不要でリモートコード実行が可能です。
パッチ適用が根本対策ですが、レンタルサーバやVPSの環境によっては、PHPのバージョンアップがすぐにはできない場合があります。共用レンタルサーバではそもそもPHPバージョンの制御権がユーザにないケースもあるでしょう。
この記事では、パッチ適用までの緊急防御策として、日本国内のIPアドレス以外からのHTTP/HTTPSアクセスを遮断する方法を紹介します。
対象読者
- 日本国内向けのWebサイト(WordPress、EC-CUBE、その他PHPアプリ)を運営している方
- サイトが日本語のみで、海外からの正規アクセスがほぼない方
- VPS・専用サーバ・クラウドインスタンスでroot権限がある方
この対策の位置づけ
この方法はPHPの脆弱性そのものを修正するわけではありません。 あくまで攻撃元の大部分を遮断する「攻撃面の縮小」です。
PHP2Shellの野良exploit試行は公開直後から世界中のIPアドレスで観測されています。現実のインターネット攻撃の大半は海外からの無差別スキャンとボットです。これをTCPレベルで遮断するだけで、攻撃に晒される確率は劇的に下がります。
パッチ適用が最優先であることは変わりません。パッチが適用できたあとも、この設定は将来の未知の脆弱性(ゼロデイ)に対する恒久的な防御層として機能します。
データソース:nami.jp の国別IPアドレスリスト
国別のIPアドレスリストは複数のソースがありますが、この記事では nami.jp が提供する国別IPv4アドレス割り当てリストを使用します。
このリストはAPNIC(アジア太平洋地域のインターネットレジストリ)等のRIRが公開する公式の割り当てデータに基づいています。「このIPブロックはどの国の組織に割り当てられたか」という情報なので、MaxMind GeoLite2のような商用GeoIPデータベースとは性質が異なります。
筆者の環境では、MaxMind GeoLite2のJPデータにIIJ系のIPレンジが含まれておらず、正規ユーザが弾かれる問題が発生しました。nami.jpのリストではこの問題は起きていません。RIRの割り当てデータのほうが、「日本の組織に割り当てられたIP」を網羅するという目的には適しています。
方法A:ufw + iptables(Ubuntu/Debianでufwを使用している場合)
多くのVPS環境ではufwが標準で有効になっています。この方法はufwのルール評価の前にiptablesのカスタムチェーンを挿入します。
1. 作業ディレクトリとスクリプトの作成
mkdir -p ~/ip_drop_except_jp
cd ~/ip_drop_except_jp
IPリストを取得してiptablesルールを生成するスクリプトを作成します。
cat > drop_except_jp.sh << 'EOF'
#!/bin/sh
IPTABLES=iptables
EXCLUDE_PORTS=80,443
wget -q -N http://nami.jp/ipv4bycc/cidr.txt.gz
gunzip -q -f -c cidr.txt.gz > cidr.txt
if [ ! -s cidr.txt ]; then
echo "ERROR: cidr.txt is empty or missing" >&2
exit 1
fi
JP_COUNT=$(grep -c "^JP" cidr.txt)
echo "JP CIDR entries: $JP_COUNT" >&2
if [ "$JP_COUNT" -lt 100 ]; then
echo "ERROR: too few JP entries ($JP_COUNT), aborting" >&2
exit 1
fi
echo $IPTABLES -F DROP_EXCEPT_JP
echo $IPTABLES -A DROP_EXCEPT_JP -p tcp -m multiport --dport $EXCLUDE_PORTS -m state --state NEW -j RETURN
# ローカルネットワークからのアクセスを許可(環境に合わせて変更してください)
echo $IPTABLES -A DROP_EXCEPT_JP -s 192.168.0.0/16 -j RETURN
echo $IPTABLES -A DROP_EXCEPT_JP -s 10.0.0.0/8 -j RETURN
echo $IPTABLES -A DROP_EXCEPT_JP -s 172.16.0.0/12 -j RETURN
# 日本国内のIPを許可
sed -n 's/^JP\t//p' cidr.txt | while read address; do
echo $IPTABLES -A DROP_EXCEPT_JP -s $address -j RETURN
done
# 上記以外を遮断
echo $IPTABLES -A DROP_EXCEPT_JP -j DROP
EOF
chmod +x drop_except_jp.sh
2. ルールの生成
sh drop_except_jp.sh > after.init.drop_except_jp
3. ufw設定ファイルの編集
/etc/ufw/before.rules の最終行の COMMIT の前に以下を追加:
:DROP_EXCEPT_JP - [0:0]
-A DROP_EXCEPT_JP -j ACCEPT
-A ufw-before-input -j DROP_EXCEPT_JP
/etc/ufw/after.init の start) と stop) を編集:
start)
/etc/ufw/after.init.drop_except_jp
;;
stop)
iptables -F DROP_EXCEPT_JP
;;
実行権限を付与:
sudo chmod +x /etc/ufw/after.init
4. ルールファイルの配置と適用
sudo mv ./after.init.drop_except_jp /etc/ufw/
sudo chmod +x /etc/ufw/after.init.drop_except_jp
sudo chown root:root /etc/ufw/after.init.drop_except_jp
sudo systemctl restart ufw
5. 確認
sudo iptables -L DROP_EXCEPT_JP | head -20
sudo iptables -L DROP_EXCEPT_JP | wc -l
数千行のルールが表示されれば成功です。
方法B:nftables(Debian 13 / trixie以降、ufwなしの環境)
Debian 13以降ではnftablesが標準です。nftablesはIPセットをネイティブにサポートしているため、iptablesで1行1ルールだったものがセット1つで済み、メモリ効率とパフォーマンスが優れています。
1. 作業ディレクトリとスクリプトの作成
mkdir -p ~/ip_drop_except_jp
cd ~/ip_drop_except_jp
sudo mkdir -p /etc/nftables.d
cat > generate_geo_filter.sh << 'EOF'
#!/bin/bash
set -euo pipefail
WORKDIR="$(dirname "$(readlink -f "$0")")"
cd "$WORKDIR"
OUTPUT="/etc/nftables.d/geo_filter.nft"
# Download JP CIDR list
wget -q -N http://nami.jp/ipv4bycc/cidr.txt.gz
gunzip -q -f -c cidr.txt.gz > cidr.txt
if [ ! -s cidr.txt ]; then
echo "ERROR: cidr.txt is empty or missing" >&2
exit 1
fi
# Count JP entries for sanity check
JP_COUNT=$(grep -c "^JP" cidr.txt)
echo "JP CIDR entries: $JP_COUNT"
if [ "$JP_COUNT" -lt 100 ]; then
echo "ERROR: too few JP entries ($JP_COUNT), aborting" >&2
exit 1
fi
# Generate nftables ruleset
{
cat << 'HEADER'
# GeoIP filter: allow JP + local, drop others on web ports
# Generated by generate_geo_filter.sh
# Source: http://nami.jp/ipv4bycc/
table ip geo_filter {
set jp_cidrs {
type ipv4_addr
flags interval
auto-merge
elements = {
HEADER
sed -n 's/^JP\t//p' cidr.txt | sed 's/$/,/'
cat << 'FOOTER'
}
}
chain input {
type filter hook input priority -1; policy accept;
tcp dport { 80, 443 } ct state new ip saddr 10.0.0.0/8 accept
tcp dport { 80, 443 } ct state new ip saddr 192.168.0.0/16 accept
tcp dport { 80, 443 } ct state new ip saddr 172.16.0.0/12 accept
tcp dport { 80, 443 } ct state new ip saddr @jp_cidrs accept
tcp dport { 80, 443 } ct state new drop
}
}
FOOTER
} > "$OUTPUT"
# Apply
nft delete table ip geo_filter 2>/dev/null || true
nft -f "$OUTPUT"
echo "Loaded $JP_COUNT entries"
echo "Done: $(date)"
EOF
chmod +x generate_geo_filter.sh
2. 実行
sudo bash generate_geo_filter.sh
JP CIDR entries: 3202(2026年10月時点)のような出力が表示されれば成功です。
3. 動作確認
# ルールの確認
sudo nft list chain ip geo_filter input
# 特定のIPがJPセットに含まれるか確認(自分のIPで試してください)
sudo nft get element ip geo_filter jp_cidrs '{ あなたのIPアドレス }'
4. 永続化
/etc/nftables.conf の末尾に追加:
include "/etc/nftables.d/geo_filter.nft"
nftablesサービスを有効化:
sudo systemctl enable nftables
sudo systemctl start nftables
5. 定期更新(cron)
IPアドレスの割り当ては変わるため、週1回の更新を設定します。
echo '0 4 * * 1 root /bin/bash /home/あなたのユーザ名/ip_drop_except_jp/generate_geo_filter.sh >> /var/log/geo_filter_update.log 2>&1' | sudo tee /etc/cron.d/geo_filter_update
特定の海外IPを許可したい場合
CDNやSaaS(Cloudflare、AWS等)からのアクセスが必要な場合は、JPリストの前にそのIPレンジを追加してください。
nftablesの場合、chainブロック内のローカルネットワーク行の前後に追加します:
tcp dport { 80, 443 } ct state new ip saddr 追加したいCIDR accept
iptablesの場合、drop_except_jp.sh のローカルネットワークRETURN行の後に追加します:
echo $IPTABLES -A DROP_EXCEPT_JP -s 追加したいCIDR -j RETURN
注意事項
- この方法はパッチの代替ではありません。 PHPのアップデートが根本対策です
- 海外からの正規アクセスがある場合は使えません。 英語版サイトがある、海外のAPIと連携している等の場合は、個別にIPレンジを許可する必要があります
- 国内IPからの攻撃は防げません。 国内の踏み台サーバ経由の攻撃には無効です
- IPv6は対象外です。 この記事の手順はIPv4のみです。IPv6でWebサービスを公開している場合は別途対策が必要です(多くのVPS環境ではIPv4のみで運用しているケースが多いですが、確認してください)
- SSHやメール等の他ポートには影響しません。 この設定はポート80/443のみを対象としています
-
priority -1(nftables版) により、他のnftablesルールより先に評価されます。既存のファイアウォール設定との競合が心配な場合は、sudo nft list rulesetで全体を確認してください - Let’s Encryptを利用している場合、ACME認証はポート80で行われ、認証サーバは海外にあります。ポート80もフィルタ対象にすると証明書の自動更新が失敗するため、対象はポート443のみにするか、ポート80のフィルタからLet’s EncryptのIPレンジを除外してください。
nami.jp のIPリストについて
この記事で使用している nami.jp のIPv4国別アドレス割り当てリストは、個人の方が長年にわたって維持されている貴重なリソースです。大量の自動アクセスは避け、必要な頻度(週1回程度)での取得にとどめてください。
まとめ
PHP2Shellは深刻な脆弱性ですが、日本国内向けサイトであれば「海外IPを全部弾く」という単純な対策が大きな効果を持ちます。パッチ適用までの緊急防御として、あるいは将来のゼロデイに備えた恒久的な防御層として、検討してみてください。