5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP2Shell緊急対策:日本国内向けサイトで海外IPを全部弾く、即効性のあるIPフィルタ防御

5
Last updated at Posted at 2026-10-23

はじめに

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を全部弾く」という単純な対策が大きな効果を持ちます。パッチ適用までの緊急防御として、あるいは将来のゼロデイに備えた恒久的な防御層として、検討してみてください。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?