defense
php2shell defense playbook
本番運用向け防御プレイブック: Nginxゼロダウンタイム防御、WAF仮想パッチとPHP-FPMサンドボックス堅牢化
php2shell.run Research Lab
•
本番運用向け防御プレイブック: Nginxゼロダウンタイム防御、WAF仮想パッチとPHP-FPMサンドボックス堅牢化
<time datetime="2026-10-23">検証日: 2026年10月23日</time> · 運用環境: Nginx 1.24+, PHP-FPM, Systemd 245+
高トラフィックを捌く本番Web環境において、単一のパッケージ更新のためにサービスを停止することは許されません。また、巨大な分散クラスターでは、全ノードのPHPランタイム更新が完了するまでに数時間から数日のリードタイムを要することがあります。
本稿では、インフラSREおよびDevOpsチーム向けに、サービス無停止で即日完結する境界遮断から、Linuxカーネル保護機能を活用したPHP-FPMプロセスの完全隔離サンドボックス化まで、段階的な防御プレイブックを提供します。
1. 段階的防御ロードマップ (Phase 1 to Phase 3)
┌─────────────────────────────────────────────────────────────┐
│ Phase 1: 境界レイヤーの即時遮断 (所要時間: 10分) │
│ - Nginx リバースプロキシでの不正パラメータ破棄 │
│ - Cloudflare / AWS WAF での仮想パッチ投入 │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Phase 2: ランタイム構成の無害化 (所要時間: 30分) │
│ - php.ini の session.upload_progress.enabled = Off 反映 │
│ - PHP-FPM デーモンの Graceful Reload (接続切断ゼロ) │
└──────────────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Phase 3: ホストOSカーネルサンドボックス化 (所要時間: 1時間) │
│ - Systemd によるファイルシステム保護 (ProtectSystem=strict)│
│ - NoNewPrivileges=yes および 権限剥奪 │
└─────────────────────────────────────────────────────────────┘
2. Phase 1: Nginx境界での即時パッチ設定
Nginxがリバースプロキシとして稼働している場合、PHP処理系にリクエストが到達する前の段階で、マルチパートストリーム内の危険なパラメータを検知・ドロップします。
/etc/nginx/snippets/php2shell-block.conf:
# POSTリクエストかつmultipart/form-dataの場合のみ検査
if ($request_method != POST) {
set $test_progress 0;
}
# リクエストボディのバッファリング
client_body_buffer_size 128k;
# Coraza WAFまたはLuaモジュールが利用可能な場合
# body_filter_by_lua_block または ModSecurity ディレクティブ
もしNginxにModSecurity/Corazaモジュールが組み込まれている場合は、以下のルールを有効化します:
SecRule REQUEST_HEADERS:Content-Type "@rx (?i)multipart/form-data" \
"id:2026101,phase:2,t:none,chain,deny,status:403,log,msg:'PHP2Shell Blocked at Nginx Layer'"
SecRule REQUEST_BODY "@rx (?i)name=[\"']?PHP_SESSION_UPLOAD_PROGRESS"
構文チェックと無停止リロード:
sudo nginx -t && sudo systemctl reload nginx
3. Phase 2: 全PHPプールの一括安全設定
AnsibleやShellスクリプトを用いて、全ホストのPHP設定を一括更新します。
Ansible タスク定義例 (tasks/mitigate_php2shell.yml):
- name: Disable vulnerable session.upload_progress
ansible.builtin.lineinfile:
path: "{{ item }}"
regexp: '^;?\s*session\.upload_progress\.enabled\s*='
line: 'session.upload_progress.enabled = Off'
state: present
loop:
- /etc/php/8.3/fpm/php.ini
- /etc/php/8.2/fpm/php.ini
- /etc/php/8.1/fpm/php.ini
notify: Reload PHP-FPM
- name: Verify PHP-FPM configuration
ansible.builtin.command: php-fpm8.3 -t
changed_when: false
4. Phase 3: SystemdによるPHP-FPMの完全サンドボックス化
万一、新たなバイパス手法によってPHPプロセス内でRCEが成立した場合でも、侵入者がOS全体に被害を広げられないよう、systemdのセキュリティプロファイルでプロセスを厳重に封じ込めます。
/etc/systemd/system/php8.3-fpm.service.d/override.conf:
[Service]
# ルートファイルシステムを完全読み込み専用化 (/var/www 等の必要な場所のみ書き込み許可)
ProtectSystem=strict
ReadWritePaths=/var/www /var/log/php /tmp /var/lib/php/sessions
# /home や /root へのアクセスを完全不可視化
ProtectHome=true
# 新たな特権昇格 (setuid) をカーネルレベルで遮断
NoNewPrivileges=true
# カーネルモジュールおよび制御パラメータの改変禁止
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
# デバイスファイルへのアクセス制限 (/dev/null, /dev/urandom 等のみ許可)
PrivateDevices=true
# 独立した空の /tmp ディレクトリを割り当て (他プロセスとの共有遮断)
PrivateTmp=true
ユニット設定の反映と適用:
sudo systemctl daemon-reload
sudo systemctl restart php8.3-fpm
この設定により、攻撃者が system("cat /etc/shadow") や system("apt install ...") を実行しても、権限昇格や重要システムファイルの読み取りがOSカーネルによって物理的に拒絶されます。