はじめに
2026-10-21、PHP プロジェクトが 認証なしでリモートコード実行(RCE)が可能な脆弱性を 2 件 公表しました(php.net のアナウンス)。
- CVE-2026-109618(GHSA-w8hw-w9fx-j79w): メモリ不足(OOM)の処理が、内部のリクエストデータの初期化途中で PHP を中断させ、壊れたデータがクリーンアップ時に正しいデータとして扱われる
- CVE-2026-109619(GHSA-6wmf-4rv5-p3rp): 解放済みのメモリを指すリクエスト用の内部状態が残り、同じワーカーが処理する次のリクエストでそれが使われる
影響を受けるのは、Web リクエストを処理するすべての PHP(PHP-FPM、Apache mod_php、php-cgi など)です。advisory は mod_php のリスクが最も高い としており、アプリケーションのコードが動く前に攻撃が成立する実証コードもあるとしています。
修正版が出たのは PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 だけです。PHP 7.4 は EOL なので修正版は出ません。ただ、深刻度を考慮して、PHP セキュリティチームが 7.4 / 8.0 / 8.1 向けに 最小限のソース backport を公開しています。
一方で、Docker の公式イメージ php:7.4-* が再ビルドされることはありません。FROM php:7.4-apache のまま運用しているシステムは、何もしなければ脆弱なまま です。
そこで、この backport を当てて再ビルドした php:7.4-* 互換のイメージを OSS として公開しました。
GitHub: softjapan/php74-cve-patched
このイメージは「応急処置」です。PHP 7.4 を安全なバージョンにするものではありません。
- 直るのは CVE-2026-109618 と CVE-2026-109619 の 2 件だけ です。7.4.33 より後に見つかったほかの問題は直っていません。
- ベース OS の Debian 11 (bullseye) は 2026-08 に LTS が終わっています。OpenSSL や Apache などの OS パッケージは今後更新されません。
- PHP プロジェクトや Docker 社の公式イメージではありません。
恒久的な対策は PHP 8.2 以降への移行 です。このイメージは、移行までの時間を稼ぐために使ってください。
TL;DR
- FROM php:7.4-apache
+ FROM ghcr.io/softjapan/php74-cve-patched:7.4-apache
-
FROMを 1 行差し替える(apache / fpm / cli、amd64 / arm64) - イメージを再ビルド・再デプロイし、PHP のワーカーをすべて再起動する
-
check-patched.shで修正が入ったことを確認する - PHP 8.2 以降への移行計画を立てる(ここまでやって対応完了です)
まず、自分の環境が脆弱か確認する
困ったことに、backport を当てても php -v は 7.4.33 のまま です。バージョン番号を見ても、対策済みかどうかは分かりません。
そこで、PHP のバイナリの中身を調べて判定するスクリプトを用意しました。
curl -fsSLO https://raw.githubusercontent.com/softjapan/php74-cve-patched/main/scripts/check-patched.sh
sh check-patched.sh php:7.4-apache # イメージ
sh check-patched.sh --container my-web-1 # 起動中のコンテナ
sh check-patched.sh --local # このマシン(VM や物理サーバー)
公式の php:7.4-apache を調べた結果です。
Image: php:7.4-apache
PHP version: 7.4.33
VULNERABLE /usr/lib/apache2/modules/libphp7.so
VULNERABLE /usr/local/bin/php
RESULT: VULNERABLE to CVE-2026-109618 / CVE-2026-109619
パッチを当てたイメージではこうなります。
Image: ghcr.io/softjapan/php74-cve-patched:7.4-apache
PHP version: 7.4.33
patched /usr/lib/apache2/modules/libphp7.so
patched /usr/local/bin/php
RESULT: PATCHED (backport found in every PHP binary)
- 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 以降は修正済みと判定します。それより古いバージョンは、backport で追加される関数
zend_new_array_singletonがバイナリにあるかどうかで判定します。 - CLI だけでなく、実際にリクエストを処理する
libphp7.so(mod_php)やphp-fpmも個別に調べます。CLI だけ直っていて Apache が古いまま、という状態も見つけられます。 - 終了コードは
0が修正済み、1が脆弱です。CI に組み込んで、脆弱なイメージをデプロイしないようにもできます。
この判定は簡易的なものです。ディストリビューションや LTS ベンダーが独自の方法で backport したパッケージは、「脆弱」と判定されることがあります。その場合はベンダーの情報を確認してください。
使い方
同じイメージを GHCR と Docker Hub の両方で公開しています。タグもダイジェストも同じです。
| 公式イメージ | GHCR | Docker Hub |
|---|---|---|
php:7.4-apache |
ghcr.io/softjapan/php74-cve-patched:7.4-apache |
softjpn/php74-cve-patched:7.4-apache |
php:7.4-fpm |
ghcr.io/softjapan/php74-cve-patched:7.4-fpm |
softjpn/php74-cve-patched:7.4-fpm |
php:7.4-cli |
ghcr.io/softjapan/php74-cve-patched:7.4-cli |
softjpn/php74-cve-patched:7.4-cli |
公式イメージと同じ使い方ができることを重視しています。
-
docker-php-ext-install/docker-php-ext-configure/pecl/php.ini-productionがそのまま使えます -
php -mの拡張モジュール一覧が公式イメージと同じであることを CI で確認しています -
apt-get installも使えます(apt の参照先をarchive.debian.orgに変えてあります)
Laravel 向けの例です。
FROM ghcr.io/softjapan/php74-cve-patched:7.4-apache
RUN apt-get update && apt-get install -y --no-install-recommends \
libzip-dev libpng-dev libjpeg-dev libfreetype6-dev \
&& rm -rf /var/lib/apt/lists/* \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j"$(nproc)" gd pdo_mysql zip bcmath opcache \
&& a2enmod rewrite
イメージを差し替えただけでは終わりません。 実行中のコンテナやワーカーは古いバイナリのまま動いています。再ビルド・再デプロイして、PHP のワーカーをすべて再起動してください。最後に check-patched.sh --container <名前> で、動いているコンテナ が修正済みかを確認してください。
何を変えたのか
変更は最小限にしています。docker-library/php に残っている 最後の PHP 7.4 用 Dockerfile(commit 1361ba95) を元に、変えたのは次の 3 点だけです。
-
apt の参照先を
archive.debian.orgに変更: bullseye は通常のミラーから消えたため、apt-get updateが 404 になります -
PHP 7.4.33 のソースに backport を適用:
patch -p1 --fuzz=0で当てるので、少しでもずれていればビルドが失敗します -
修正が入ったことをビルド中に確認:
php、php-fpm、libphp7.soのどれか 1 つでも修正が入っていなければ、ビルドを失敗させます
当てているパッチは、上流の php/php-src@712e60f と バイト単位で同一 です。CI が push のたびと毎週、次の点を確認しています。
- 同梱のパッチが上流の commit と同一であること
- 上流の backport ブランチに新しい commit が入っていないこと(入っていれば CI が失敗して知らせます)
当初は社内で php:7.4-apache の上から ./configure をやり直す方法をとっていました。しかしこの方法には、configure のオプションが x86_64 向けに固定される、fpm / cli 版でオプションがずれる、といった問題がありました。公開版では公式の Dockerfile そのものを元にして、マルチアーキテクチャへの対応と公式との互換性を両立しています。
信頼して使うために
セキュリティ目的のイメージを、知らない人が配っているものから取ってくるのは不安だと思います。そのため、検証できる材料をそろえました。
- イメージはリポジトリの GitHub Actions だけでビルドし、SBOM と provenance を付けています
- cosign(keyless)で署名しています
cosign verify ghcr.io/softjapan/php74-cve-patched:7.4-apache \
--certificate-identity-regexp '^https://github.com/softjapan/php74-cve-patched/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
- 本番環境ではダイジェスト(
@sha256:...)で固定してください - このイメージを使わず、自分でビルドする手順 も公開しています。Docker を使わずソースからビルドしている環境向けに、
patch -p1で当てる手順も載せています
イメージをすぐ差し替えられない場合の緩和策
advisory には、すぐに更新できない場合の緩和策も載っています。どれも攻撃の経路を一部ふさぐだけで、安全になるわけではない と明記されていますが、差し替えまでのつなぎや多層防御として使えます。
CVE-2026-109618(OOM による状態破損)
-
memory_limit=-1を、アプリケーションが上書きできない方法(FPM プールや Apache のphp_admin_value)で設定する。どこかの設定で有限の値に戻されていないかも確認する- その代わり、コンテナや cgroup のメモリ制限で、影響を受けたワーカーだけが止まるようにしておく
-
RLIMIT_AS/RLIMIT_DATAでの代用はしない(脆弱な経路を逆に通る原因になる)
-
post_max_sizeを小さくする。アップロードがないなら8M、あるなら32M。upload_max_filesizeはpost_max_sizeより小さくする - Web サーバーやプロキシでも、リクエストサイズ、フィールド数、マルチパートの数、ヘッダー数などを制限する。
Content-Lengthがないリクエストも対象に含める
CVE-2026-109619(古いグローバル状態)
-
session.upload_progress.enabled=0を設定し、ワーカーをすべて再起動する-
php.ini、FPM プール、サーバー設定など、リクエストの解析前に効く場所で設定する。ini_set()や.user.iniでは効果がない
-
- 初期化前に警告を出すような、無効な設定や非推奨の設定をリクエストごとに適用しているなら取り除く(
@やdisplay_errorsで表示を隠しても意味はない) - 攻撃者が起こせる fatal error やタイムアウトを修正する
このイメージには、上記の一部(session.upload_progress.enabled = 0 と、小さめの post_max_size / upload_max_filesize)をまとめた設定ファイルを同梱しています。デフォルトでは無効 です。PHP の挙動は公式イメージと変えていないので、使う場合は次の 1 行を追加してください。
RUN cp /usr/local/share/php74-cve-patched/zz-cve-2026-mitigations.ini "$PHP_INI_DIR/conf.d/"
応急処置のあとにやること
最後にもう一度書いておきます。このイメージは止血であって、治療ではありません。
-
すべての PHP 7.4 環境を洗い出し、
check-patched.shで確認した - イメージを差し替え、再デプロイし、ワーカーを再起動した
-
動いているコンテナ で
check-patched.sh --containerがPATCHEDになった - 必要に応じて緩和策の設定も入れた
- PHP 8.2 以降への移行計画と期限を決めた(8.2 も 2026-12-31 に EOL になるので、8.3 以降も検討する)
- OS(Debian 11)も更新されないことを、関係者と共有した
まとめ
- CVE-2026-109618 / 109619 は、認証なしの RCE です。特に mod_php は最優先で対応してください
- PHP 7.4 の公式 Docker イメージは今後も直りません。
php:7.4-*を使っていれば脆弱なままです -
php74-cve-patched は、
FROMを 1 行変えるだけで公式の backport を取り込める 応急処置 です - 恒久的な対策は PHP 8.2 以降への移行です
PHP 8.0 / 8.1 版(こちらも公式の backport があります)や Alpine 版も検討しています。必要な方は Issue で教えてください。この記事が役に立ったら、Qiita の LGTM や GitHub の ⭐ をもらえるとうれしいです。同じように困っている人に見つけてもらいやすくなります。