PHP・証明書・カーネル、1週間に3つの更新が重なった記録

10月21日の昼、同じように Claude Code を動かしていて、X などの情報に気づくのも早い知り合いから Discord で、PHP に重大なセキュリティの更新が来ると聞きました。セキュリティにすごく詳しいというわけではないのですが、log4j のときも教えてくれた人で、今回も早めに知れて助かりました。その夜に PHP を止めて、直った版を待って、上げて確かめるところまでやっています。

同じ週に証明書の件もあり、OS とカーネルの更新まで重なりました。PHP を上げたら終わり、とはいかなかったので、家のサーバーでやったことを順に残しておきます。

止めた理由

PHP の穴は CVE-2026-109618 と CVE-2026-109619 の2つで、どちらも CVSS v4 で 9.2。メモリが足りなくなったときの後始末で、中途半端なデータを正しいものとして扱ってしまうのが1つ目、前のリクエストの中身が同じプロセスの次のリクエストに残るのが2つ目です。

どちらもログインなしで外からコードを実行されうるもので、PHP-FPM も対象になっている。カナダのサイバーセキュリティセンターの注意喚起(AL26-026)では、実証コードがあり、その時点で悪用は未確認とされていました。

公式には設定での回避策も載っていましたが、「完全ではない、完全なのは更新だけ」と書いてあった。それなら直るまで PHP ごと止めようと決めて、22時半ごろに php-fpm を止めました。PHP のページは 502 になりますが、それ以外はそのまま動きます。

更新待ち

PHP 本体は21日の22時40分ごろに、8.5.12・8.4.27・8.3.36・8.2.35 が出ました。ただ、本体が出ても自分の環境に入るパッケージが出るまでは待つ必要があります。

私は RHEL 系で、サードパーティのリポジトリから PHP を入れています。OS 標準のパッケージは遅れるし、当日の公式の告知には、使っているリポジトリは今日は更新しないと書いてあった。これは朝まで止めたままかな...と思って、版を10分ごとに見る見張りを置きました。

ところが23時台に、そのリポジトリに 8.3.36 が来ました!結局、0時1分に更新して、0時2分に php-fpm を起動しています。

確認したこと

更新後は、版の表示だけで終わりにせず5つ確かめました。パッケージが新しくなったことと、実際に動いているものが新しくなったことは、分けて見ておきたい。

1つ目はパッケージの版。RHEL 系は数字を上げずに修正を取り込むことがあるので、版だけでは分からない場合は更新履歴に今回の CVE 番号があるかを見る。

2つ目は、動いている php-fpm が更新後に起動したものか。パッケージを上げても、古いプロセスが動き続けていたら中身は古いコードのままです。今回のようにリクエストをまたいで中身が残る穴だと、起動し直したかも気になります。

確認に使うコマンドの例はこの2行です。

rpm -q --changelog php-common | grep CVE-2026-10961
ps -o pid,lstart,cmd -C php-fpm

3つ目は拡張が欠けていないか。PHP 本体が上がっていても、使っていた拡張がそろっていなければ、アプリがこれまでどおり動くかは別です。

4つ目は、別の古い PHP が残っていないか。昔入れた別の版や、コンテナの中の PHP も含めて見ています。普段使っている PHP だけ確かめて終わると、ほかに置いた分を忘れそうなので。

5つ目は、レスポンスのヘッダーに PHP の版が出ていないか。更新できたかとは別に、外から見える情報も確かめました。

この5つが全部そろったのが0時14分。ここでやっと寝られた。

翌日と23日の案内

翌日、「直しは不完全らしい」と聞いて、また焦りました。公式のお知らせ、GitHub のアドバイザリ、ソースの 8.3 の枝まで見ましたが、追加の修正はありませんでした。不完全とされていたのは設定の回避策の方で、公式が最初から完全ではないと書いていたものです。

その後、10月23日には公式から別の穴(CVE-2026-115560)の追加の案内が出ています。こちらは設定とアプリの書き方が、かなり限られた組み合わせになっているときだけ起きるものだった。

session.serialize_handler が php_binary か php_serialize の状態から、実行中に ini_set で php へ切り替えて session_start するアプリで、upload_progress が有効な場合が対象です。次の定例の版で直すので、それまでは session.upload_progress.enabled=0 を推奨、という案内でした。

これは21日の回避策にも入っていた設定です。翌日に調べた「不完全」と、23日に追加された案内は、日付も中身も分けて読んでおく必要がありました。

証明書の件

同じ週には、インドの認証局 eMudhra の中間CA の秘密鍵が漏れる件もありました。10月20日の公式発表では、作り方の誤りで証明書から誰でも秘密鍵を復元できる状態が、2025年4月から続いていたとのこと。対象は8つで、TLS が6つ、コード署名が2つです。

主要ブラウザは17〜20日にブロック済みでしたが、ブラウザ以外にも証明書を確かめるものはある。自宅の機械で調べると、OS の証明書の束を使う curl はその鎖を信頼していました。ブラウザで拒否されるから大丈夫、では済まなかった。

その時点では OS の修正版パッケージがまだ無かったので、RHEL 系の暫定回避として、emSign ECC Root CA - G3 と AAA Certificate Services の2つのルートをブロック対象に置き、update-ca-trust で反映しました。確かめ方は、漏れた鎖でつないだときに拒否されるか。curl なら verify のエラーになるかを見ます。

ちなみに Python の仮想環境で使う certifi は、OS の束とは別物です。こちらは8つを 2026.10.19 に上げています。OS 側を直したところで終わると、別の束を使っているものが残る。

Docker のコンテナも中に自分の束を持つので、ホスト側の変更だけでは効きません。19個のうち外へ通信する7つを対象に、上流が直したイメージを待って作り直す方針にして、そのあと入れ替えました。

OS とカーネルの更新

PHP を入れるときの依存で、OS のリリースの版だけ先に上がっていました。未適用の更新は300件ちょっとあり、Critical が7件、Important が200件。PHP の更新だけ見ていたら、OS 側にもこれだけあった。

先に再起動なしでできる分として、カーネルを除いたセキュリティの更新を入れ、古いライブラリのまま動いているサービスを1つずつ再起動する順にしました。

一度に全部入れなかったのは、マイナー版が上がると古い版が置き場に無く、ほぼ戻せないためです。ここは別の機械で試して確かめました。入れる前の状態へ戻せるつもりで進めるのは怖い。

カーネルは夜のメンテの時間を決めて、事前の確認、更新、再起動、事後の確認という順にしています。新しいカーネルでは1回だけ起動し、固まったら古いカーネルに戻る形を用意しました。結局戻す必要はなく、再起動も無事に終わりました。

次から見るところ

家でサーバーを動かしていると、今回のように知り合いや X で更新を知ることもあります。ただ、速報を見ただけでは自分のところに当たるか分からないので、直った版と対象は公式の告知で確かめるようにしたい。PHP なら php.net のニュース、OS なら配布元のセキュリティ情報を見ます。翌日に「不完全」と聞いて焦った件も、何が不完全なのかを公式の説明まで戻って読んで落ち着きました。

自分の版は php -v で見られますが、Docker を使っているとホストで見ただけでは足りません。コンテナの中は別なので、中でも版を確かめます。

docker exec <コンテナ名> php -v

ホストを上げても中は古いままで、イメージを取り直すか作り直し、コンテナを入れ替えるところまで必要です。証明書の件でも、ホスト側が済んだところで終わると、中に残った分を見落とす。今回もコンテナを入れ替えるところまでやっています。

ちなみに、パッケージが新しくなっていても、動いているプロセスは古いライブラリを使い続けることがあります。RHEL 系なら、OS の再起動が要るかと、再起動が要るサービスを次の2行で見られます。

dnf needs-restarting -r
dnf needs-restarting -s

版を見て安心したあとに、まだ動いている方を見忘れないようにしたい。PHP の起動時刻を見たり、OS の更新後にサービスを1つずつ再起動したりしたのも、この確認でした。

直った版がまだ無ければ、止められるものは今回の PHP のように止めて待つことになります。止められないものは、前段で入口を絞ってつなぐ手もあります。ただ、その状態で済ませず、直った版が来たら作り直して入れ替えるところまで見ておきます。

入れる前には、戻す方も確かめておきたい。OS のマイナー版を上げたあとに古い版へ戻せるかは、別の機械で試して初めて分かりました。カーネルも古いものを残し、新しい方で1回だけ起動する形にしています。更新があると早く入れたくなりますが、再起動したあとに戻れないと困るので、ここは先に見ておいてよかったです。

再起動の前提条件

準備中には、データベースのデータを置くディスクが、起動時にマウントされなくても止まらない設定になっていることも見つかりました。起動の順番の守りが無く、マウントより先にデータベースが上がると、空のデータベースを作るおそれがある。

そこで「このディスクがマウントされてから」という条件を足しています。更新するパッケージだけ見ていたら、この起動順は確認しないままだったと思います。再起動の前に見つかってよかった。

この記事は、2026年10月に note に書いたものをこちらへ移しました。

ブログのトップへ