重大脆弱性への備えから考える、開発と運用の境界をなくすセキュリティ体制
PHP 本体や利用しているライブラリに遠隔コード実行(RCE)の脆弱性が公表されると、実証コード(PoC)の公開や探索スキャンが数日のうちに始まることも珍しくありません。攻撃リクエストが届く前に本番環境へ修正パッチを反映できなければ、サーバーの遠隔操作や情報漏洩を許すことになります。
しかし、本番への反映手順やテスト工程が日常業務として整備されていない現場では、検証工数への懸念から更新判断が先送りされ、サーバーが無防備な状態に置かれ続けます。
PHP2Shell が突きつけた運用の現実
2026年10月に公表された PHP の脆弱性 CVE-2026-109618 と CVE-2026-109619(通称 PHP2Shell)は、2つの欠陥を組み合わせて悪用されるものです。認証のない外部の第三者が、任意のコードを実行できるおそれがあります。
1つ目は、メモリ不足のときのエラー処理の欠陥です。リクエストの処理を途中で打ち切ると、メモリが壊れたまま残ってしまいます。2つ目は、致命的エラーで止まったリクエストの状態がワーカーに残る欠陥です。これにより、次のリクエストが解放済みのメモリを参照してしまいます。Apache の mod_php 構成ではリモートコード実行まで確認されています。PHP-FPM もソースを解析した限りでは影響を受けるとされており、Linux で一般的に使われている構成もそのまま対象になります。
この事例が示したのは、自社で開発したコードに不備がなくても、実行環境である PHP 本体の欠陥によってサーバーの遠隔操作を許すという事実です。しかも今回は、設定変更による回避策がどれも完全ではありません。根本的な対策は、修正版(8.2.35 / 8.3.36 / 8.4.27 / 8.5.12)への更新だけです。また、サポートが終了した PHP 8.1 以前には修正版が提供されません。本番環境の PHP ランタイムをすぐに更新できる手順がなければ、修正版が出ても無防備な状態が続きます。古いバージョンにとどまっていれば、直す手段そのものがありません。
PHP では 2024 年にも、Windows の CGI 構成を狙った CVE-2024-4577 が公表直後から悪用されました。PHP 本体だけでなく、libxml2 や cURL などリンクされている C ライブラリの欠陥によってプロセス全体が脅威に晒されるケースも定期的に発生します。ランタイムやミドルウェアの脆弱性は偶発的な事故ではなく、運用中に直面し続ける事象です。場当たり的な対処で凌ぐのではなく、脆弱性の検知から修正版の検証、本番反映までを一連の手順として日常的に回せる運用体制を整えておく必要があります。
「完成」から「継続的改善」へ
システムで稼働する OS や外部ライブラリには個別のサポート期限(EOL: End of Life)があり、運用における前提となっています。EOL を過ぎたバージョンにはセキュリティ修正が配布されないため、新たな脆弱性が発見されても開発元による修正パッチは提供されません。実際、PHP2Shell でもサポートが終了した PHP 8.1 以前には修正版が出ていません。
たとえば、PHP のフレームワークとして Laravel や Symfony を採用し、フロントエンドに Next.js や Vue を組み合わせた Web アプリケーションを考えます。この構成では、Composer と npm の依存ツリー全体で、直接・間接を合わせて数百個の外部パッケージを抱えるのが一般的です。PHP 自体も毎年新しいバージョンが公開され、それぞれ約 4 年でサポートを終えます。フレームワークにも、短いものでは 1 年半から 2 年ほどでサポート期限を迎えるものがあります。自社で書いたアプリケーションコードに改修予定がなくても、依存パッケージの EOL を回避して脆弱性パッチを受け取るには、計画的なバージョン追従が避けられません。
外部ライブラリの更新や脆弱性パッチの適用には、画面追加や業務ロジック改修のような機能的な変化が伴いません。そのため、機能開発の納期を優先するあまり、更新作業が先送りされて EOL を迎えてしまう現場は多く見られます。この先送りを防ぐには、更新にかかる検証工数を抑え、日常的な修正と同じ作業手順の中でパッチを消化できる状態を作っておく必要があります。
継続的に安全性を保つための技術的アプローチ
セキュリティ情報を手作業で巡回し、動作確認を手動で行う運用では、数百におよぶ依存パッケージの更新頻度に追いつけません。検出、多層での遮断、回帰テストの各工程を機械化し、パッチ適用にかかる作業者の判断負荷と検証工数を抑える構成をとります。
依存関係の継続的監視
依存関係の監視では、Composer や npm といった言語パッケージマネージャの管理下にあるライブラリと、Dockerfile や OS レベルで導入されるランタイムとで、異なる監視手法を組み合わせます。
アプリケーションの依存関係にあたる Composer や npm のパッケージには、Dependabot や Renovate などの自動更新ツールを導入します。脆弱性を含むバージョンが特定された時点で、修正済みバージョンへの更新プルリクエストが自動で起票されるようにしておきます。
一方、PHP ランタイム自体は Composer の依存に含まれません。コンテナ環境であれば、Dockerfile のベースイメージ(php:8.3-fpm など)のタグも Renovate の更新対象にして、PHP のパッチリリースが出た時点で更新のプルリクエストが届くようにしておきます。あわせて Trivy などのコンテナイメージスキャナを CI パイプラインやコンテナレジストリに組み込み、ベース OS に含まれる既知の脆弱性を日次で検出します。ただし、公式イメージの PHP はソースからビルドされているため、OS パッケージのスキャンでは検出できない場合があります。ランタイムのバージョンは、PHP の公式リリース情報と突き合わせて確認する運用も併せて用意しておきます。
システム全体の構成要素を一覧化した SBOM(ソフトウェア部品表)をビルドパイプラインで生成しておけば、新たな脆弱性が公表されたときに、影響を受けるコンポーネントが自社システムに含まれているかを短時間で特定しやすくなります。
多層防御の設計
修正パッチの適用前やゼロデイ攻撃への備えとして、単一の防御機構が突破されても侵害が完結しない多層防御の構造を作ります。今回の PHP2Shell のように、Linux 上の一般的な構成がそのまま対象になる脆弱性もあります。パッチを当てるまでの間も、侵害を広げない構造が必要です。
アプリケーション層での入力バリデーションに加え、前段のリバースプロキシや Web アプリケーションファイアウォール(WAF)で、リクエストサイズや multipart のパート数を制限します。そうすれば、異常なリクエストをアプリケーションに届く前に遮断できます。PHP 側でも、使っていない機能(session.upload_progress など)を無効にし、ワーカーが処理するリクエスト数を pm.max_requests で制限しておけば、攻撃が成立する条件を減らせます。
実行環境としてコンテナを採用する場合は、不要なツールやシェルを含まない最小限のベースイメージを選び、Web サーバープロセスの実行権限を制限します。PHP2Shell で実行されるコードは Web ワーカーの権限で動くため、実行権限を最小化していれば、万が一任意のコード実行を許したとしても、攻撃者による特権の奪取やコンテナ外への侵入拡大のリスクを低減できます。
テストの自動化
迅速に修正パッチを適用したくても、更新によって既存機能が壊れる懸念があれば、本番反映の判断は滞ります。回帰テストを自動化しておけば、パッチ適用に伴う障害への懸念と手作業の確認負担を軽減できます。
PHPUnit や Pest による単体・統合テストを CI パイプラインに組み込み、PHP ランタイムのパッチリリースや Laravel、Symfony のバージョン追従が発生したときに、全テストが自動で実行される環境を構築します。主要な業務フローに対する自動テストが整備されていれば、手作業による回帰検証の範囲を絞り込みやすくなり、パッチ検証にかかる工数を抑えられます。
事業・組織としてセキュリティを仕組み化するポイント
CI や自動更新ツールを導入しても、パッチ適用にかかる費用の負担区分や、誰が本番適用の承認を下すかという取り決めがなければ、実際の更新作業は止まります。技術基盤を実効性のある運用へつなげるには、発注者と開発チームのあいだで作業枠と責任の所在を定めておく必要があります。
保守費用を「品質投資」と位置づける合意形成
フレームワークや言語ランタイムのバージョンアップを「問題が起きてから対処する作業」と位置づけると、予算の確保が難航します。放置されたライブラリが EOL を迎え、メジャーバージョンが 2 世代以上離れてしまうと、後方互換性の失われた変更が積み重なります。その結果、セキュリティパッチだけを個別に取り出して適用することが技術的に難しくなります。バージョンを上げるにはコード全体の再設計が必要になり、パッチすら当てられない状態に陥ります。PHP 8.1 以前で動き続けているシステムは、PHP2Shell に対してまさにこの状況に置かれています。
バージョン更新の工数は、スプリント計画や月次の運用枠にあらかじめ一定割合(全体の 10〜20% 程度)で確保しておく必要があります。日常の運用業務として定期更新を組み込んでおけば、大規模な刷新プロジェクトを立ち上げることなく、最新のセキュリティ状態を維持しやすくなります。
定期的な棚卸しとレビュープロセスの確立
運用の現場では、日々の個別パッチ適用と並行して、数ヶ月から半年単位でシステム全体の依存関係を見直す定期的な棚卸しが必要です。受託開発の現場であれば、発注側と開発側の双方が参加する定例会などで、現在のフレームワークのサポート期限や、検知された脆弱性の対応状況を共有するプロセスを定例化します。
一度に大規模な刷新を行うことが難しくても、四半期ごとに依存パッケージの棚卸しを実施していれば、サポート終了に伴う急激な移行コストの発生を防ぎ、四半期ごとの計画的なアップデートとして実施しやすくなります。
責任分界点とデプロイフローの事前定義
重大な脆弱性が公表された直後は、対応方針の合意形成に時間が費やされがちです。次の 3 点が契約や SLA で定義されていなければ、見積もり作成や社内稟議の段階で数日が経過してしまいます。
- 発注側の承認権限者
- 開発側の着手基準
- 月額保守費用に含める作業範囲(CVSS スコア 7.0 以上の高深刻度パッチは定常業務枠で即座に適用する、など)
PHP2Shell の CVSS スコアは 9.2 で、この基準なら公表当日から適用作業に入る対象です。
こうした停滞を防ぐには、影響度の閾値に応じた判断基準をあらかじめ取り決めておく必要があります。ステージング環境での自動回帰テストから本番デプロイまでを最小の手順で完了できるパイプラインを平時から運用しておけば、緊急時にも不具合のリスクを抑えて安全に修正を適用できます。
継続的な更新を支える運用設計
Web アプリケーションの脆弱性対策では、発見された不備への修正パッチを遅滞なく本番環境へ適用・デプロイする仕組みが求められます。ランタイムやミドルウェアの欠陥による攻撃リスクを最小化するには、自動テストによる回帰検証のパイプラインと、月次の開発計画に更新工数を組み込む組織的なプロセスの双方が揃っていなければなりません。
パーティーハード株式会社では、PHP、Symfony、Laravel によるバックエンドから Next.js や Vue によるフロントエンドまで、CI/CD パイプラインとテスト自動化を組み合わせた運用設計を手がけています。リリース後も依存パッケージや実行基盤の更新を滞らせず、脆弱性の公表時に速やかにパッチを適用できる開発・保守体制の構築を支援します。

望月 涼太 / polidog
Co-Founder / Web Engineer

