
サポート切れのPHPもPHP2Shellから守りたい! 有志のバックポートでソースからビルドする手順
【追記:2026年10月28日】php-src-backportsには、PHP2Shellの修正が入っています。開発チームがサポート切れのバージョン向けに出した最小パッチで、7.4〜8.1は10月19日、7.0〜7.3は10月24日に入りました。
ただし、タグの名前(8.1.34など)は変わらずに中身だけが更新され、NEWSにも書かれていません。
それより前にダウンロードしたソースには入っていないので、「修正済みのCVEを確かめる」の方法で、手もとのソースを確かめてください。
PHPの脆弱性「PHP2Shell」(CVE-2026-109618/CVE-2026-109619)の公式の修正版は、8.2以降の4つだけ。8.1以前のPHPには、公式の修正版は出ていません。
ところが、わたくしが面倒を見ているサーバーには、7.0や7.1のまま動いているサイトがまだあるんです。全部すぐに8.2へ上げられたら、苦労しないんですけどね…。
そこで使えるのが、有志がサポート切れのPHPにセキュリティ修正を移植している「php-src-backports」。この記事では、これを使ってソースからPHPを入れ替える手順と、実際にハマったエラーの直し方をまとめました。
先に結論を書いておきます。
- 8.1以前の選択肢は「8.2以降へ上げる」「開発チームの最小パッチ」「有志のバックポート」の3つ
- ビルドの前に、session.upload_progress.enabled = 0で確認済みの経路をふさいでおく
- バックポートを使うなら、直したいCVEが入っているかを必ず確かめる
- GitHubのソースなので、ビルドにはbisonとre2cが必要
- バックポートはあくまでつなぎ。本命は8.2以降への移行
8.1以前のPHP2Shell対策
使っているPHPのバージョンで、取れる手が変わります。
| 使っているPHP | 取れる手 |
|---|---|
| 8.2〜8.5 | 公式の修正版(8.2.35・8.3.36・8.4.27・8.5.12)へ更新 |
| 7.4・8.0・8.1 | 有志のバックポート(開発チームの最小パッチ入り)を使う、またはパッチを自分で当てる |
| 7.3以前 | 有志のバックポートを使う(7.0〜7.3は10月24日に修正入り) |
8.2以降なら、迷わず公式の修正版へ。php.netから取得して、いつもどおりビルドするだけです。
開発チームの最小パッチは、今回の穴だけを直すもの。一方のバックポートは、サポートが切れたあとに見つかったほかの穴の修正も、まとめて移植されているのがポイントです(全部とは限りませんが)。
バックポートは信用できる?
有志のものと聞くと、ちょっと心配になりますよね。わたくしも、そこがいちばん気になりました。
php-src-backportsを作っているのは、Shivam Mathurさん。GitHub ActionsでPHPを入れる定番ツール「setup-php」の作者で、PHPの世界ではよく知られた人です。
ただし、個人のプロジェクトなので、次の点は覚えておきたいところ。
- 全部の穴が直るとはかぎらない(古いバージョンにうまく移せない修正もある)
- いつ直るかの約束はない
- 1人の作者に頼ることになる
なので、直したいCVEが入っているかを、自分の目で確かめてから使う。これが鉄則です。
php.iniで応急処置する
ビルドの前に、まずやっておきたいのがsession.upload_progress.enabledを止めること。ファイルのアップロード中に、進み具合をセッションに書き込む機能です。
PHP2Shellでは、この機能が確認済みの攻撃の経路になっています。設定を1行変えて再起動するだけなので、修正版が出ていない古いPHPでも、すぐに塞ぐことができるんです。
session.upload_progress.enabled = 0PHP-FPMなら、プールの設定に書く方法もあります。
php_admin_flag[session.upload_progress.enabled] = off# 再起動する
systemctl restart php-fpm # mod_php なら apachectl graceful
# php.ini に書いたときの確認(Local Value が Off ならOK)
/usr/local/php/bin/php -i | grep session.upload_progress.enabledFPMのプール設定に書いたときは、コマンドのphp -iには出てきません。phpinfo()を一時的に置いて確かめ、終わったらすぐに消しましょう。
気をつけたいのは、ini_set()や.user.iniでは効かないこと。リクエストを読む前に効く場所(php.iniやFPMのプール設定)に書く必要があります。
困るのは、アップロードの進み具合を表示する機能(PHP_SESSION_UPLOAD_PROGRESS)を使っているアプリだけ。ふつうのサイトなら、まず影響はありません。
ただし、塞ぐことのできるのは確認済みの経路だけ。ほかのやり方の攻撃は残るので、あくまで入れ替えまでのつなぎです。更新したあとも、0のままにしておくのがおすすめとのこと。
ほかの応急処置も含めて、こちらにまとめています。
【追記:2026年10月22日】攻撃はもう始まっています。Amazonの脅威情報チームによると、修正版の公開から数時間で攻撃用のコードが出回り、「すでに広く悪用されている」とのこと。公表より4日前の10月17日にも、攻撃の試みが見つかっていました。22日にはIPAとJPCERT/CCも注意喚起を出...
bisonとre2cを用意する
php.netの配布ファイルとちがって、GitHubから取ったソースにはconfigureが入っていません。自分で作るので、次のツールが必要です。
| ツール | 必要なバージョン(PHP 8.x) | 確かめ方 |
|---|---|---|
| bison | 3.0.0以上 | bison --version |
| re2c | 1.0.3以上 | re2c --version |
| autoconf | 2.68以上 | autoconf --version |
OSのパッケージで足りるかは、OSのバージョン次第です。
| OS | bison | re2c |
|---|---|---|
| CentOS 6 | × 2.4.1 → ソース | × → ソース |
| CentOS 7 | ○ 3.0.4 | × 0.14.3(EPEL) → ソース |
| RHEL 8系 | ○ 3.0.4 | × 0.14.3(PowerTools) → ソース |
| RHEL 9系 | ○ 3.7.4 | ○ 2.2(EPEL) |
| Debian 8 jessie | ○ 3.0.2 | × 0.13.5 → ソース |
| Debian 9 stretch | ○ 3.0.4 | × 0.16 → ソース |
| Debian 10 buster | ○ 3.3.2 | ○ 1.1.1 |
| Debian 11 bullseye | ○ 3.7.5 | ○ 2.0.3 |
| Debian 12 bookworm | ○ 3.8.2 | ○ 3.0 |
| Debian 13 trixie | ○ 3.8.2 | ○ 4.1 |
| Ubuntu 14.04 | ○ 3.0.2 | × 0.13.5 → ソース |
| Ubuntu 16.04 | ○ 3.0.4 | × 0.16 → ソース |
| Ubuntu 18.04 | ○ 3.0.4 | × 1.0.1 → ソース |
| Ubuntu 20.04 | ○ 3.5.1 | ○ 1.3 |
| Ubuntu 22.04 | ○ 3.8.2 | ○ 3.0 |
| Ubuntu 24.04 | ○ 3.8.2 | ○ 3.1 |
○はOSのパッケージ(yum・dnf・apt-get)で足りるもの、×は足りないのでソースからビルドするものです。Ubuntu 18.04のre2cは1.0.1で、あと少しだけ足りないのが惜しいところ。
表は、PHP 8.xでの話です。PHP 7.xは必要なバージョンがちがうので、足りないときはconfigureのエラーに出るバージョンに合わせてください。
Debian・Ubuntuなら、ビルドに使うものをまとめて入れておくと楽です。入ったら、bison --versionとre2c --versionで、上の表のバージョンを満たしているか確かめましょう。
apt-get install build-essential autoconf pkg-config bison re2c足りないものは、OSのものは置き換えずに、別の場所に入れます。bisonのビルドにはm4が必要なので、最小構成のOSなら先に入れておきます。re2cのソースは、GitHubのリリースのページから取得します。
# bison(先に m4 を入れておく)
cd /usr/local/src
curl -LO https://ftp.gnu.org/gnu/bison/bison-3.0.5.tar.gz
tar xzf bison-3.0.5.tar.gz && cd bison-3.0.5
./configure --prefix=/usr/local/bison-3.0.5 && make && make install
# re2c(https://github.com/skvadrik/re2c/releases から取得)
cd /usr/local/src
tar xf re2c-1.3.tar.xz && cd re2c-1.3
./configure --prefix=/usr/local/re2c-1.3 && make && make installこのあとの作業は、PATHを通したターミナルで行います。bisonとre2cは、configureのときだけでなく、makeの途中でも使うからです。
export PATH=/usr/local/bison-3.0.5/bin:/usr/local/re2c-1.3/bin:$PATHyumやaptが通らないとき
サポートが終わったOSは、パッケージの置き場所が移っています。リポジトリの設定が古いままだと、yum installやapt-get updateがエラーになるんです。
CentOS 7は、vault.centos.orgに向け直します。
cp -p /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.orig
sed -i -e 's|^mirrorlist=|#mirrorlist=|' \
-e 's|^#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|' \
/etc/yum.repos.d/CentOS-Base.repo
yum clean all
yum install bison m4CentOS 6は、同じように書き換えたうえで、$releaseverを6.10にします。
Debianのサポートが終わったバージョン(9 stretchなど)は、archive.debian.orgへ。
# /etc/apt/sources.list(Debian 9 の例)
deb http://archive.debian.org/debian stretch main
deb http://archive.debian.org/debian-security stretch/updates mainapt-get -o Acquire::Check-Valid-Until=false updateUbuntuは、サポートが完全に終わったバージョンがold-releases.ubuntu.comへ移ります。16.04や18.04は、有料のESMがある間はふつうの置き場所に残っています。
sed -i -e 's|//[a-z.]*archive.ubuntu.com|//old-releases.ubuntu.com|' \
-e 's|//security.ubuntu.com|//old-releases.ubuntu.com|' /etc/apt/sources.list
apt-get updateただし、こうした置き場所のパッケージには、もうセキュリティの更新が来ません。ビルドの道具を入れるためだけに使う、と割り切りましょう。
ソースからビルドする手順
ソースは、GitHubのshivammathur/php-src-backportsから取得します。PHP本体のソースをコピーして、サポート切れのバージョンに修正を移しているリポジトリです。
バージョンごとにブランチがあって、ダウンロードに使うタグは次のとおり。READMEにも同じ一覧があります。
| PHP | タグ(NEW に入れる) | ブランチ |
|---|---|---|
| 8.1 | 8.1.34 | PHP-8.1-security-backports |
| 8.0 | 8.0.30 | PHP-8.0-security-backports |
| 7.4 | 7.4.33 | PHP-7.4-security-backports |
| 7.3 | 7.3.33 | PHP-7.3-security-backports |
| 7.2 | 7.2.34 | PHP-7.2-Security-backports |
| 7.1 | 7.1.33 | PHP-7.1-security-backports |
| 7.0 | 7.0.33 | PHP-7.0-security-backports |
ダウンロードのURLは、最後のタグの部分を変えるだけです。
https://github.com/shivammathur/php-src-backports/archive/8.1.34.tar.gzタグの名前は、公式の最後のバージョンの番号のまま。修正が移されても、名前は変わらずに中身だけが更新されます。手もとのソースが新しいかは、このあとの「修正済みのCVEを確かめる」で確かめます。
取り直すときは、古いtar.gzと展開したフォルダを先に消しておきましょう。ファイル名もフォルダ名も前回と同じなので、うっかり古いほうでビルドしてしまいます。wgetは、同じ名前のファイルがあると「.1」を付けて別に保存するので要注意です。
ここからは、8.1.7を8.1.34に上げる例で書きます。ほかのバージョンなら、上の表のタグをNEWに入れるだけです。
OLD=8.1.7 # 今のバージョン(php -v で確認)
NEW=8.1.34 # インストールするバージョン(php-src-backports のタグ)
SRC=/usr/local/src/PHPビルドの全体の流れ
まずは、全体の流れです。わたくしが7.4.30を7.4.33に上げたときも、ほぼこのとおりでした。
cd $SRC
rm -rf php-$NEW-backports.tar.gz php-src-backports-$NEW # 取り直すときは、古いものを先に消す
curl -L -o php-$NEW-backports.tar.gz https://github.com/shivammathur/php-src-backports/archive/$NEW.tar.gz
tar xzf php-$NEW-backports.tar.gz && cd php-src-backports-$NEW
# 修正が入っているか(1つ目で行が出て、2つ目が1以上ならOK)
grep -nE 'rfc1867_enabled\) \|\| (true|1)\)' ext/session/session.c
grep -c 'php_mime_should_save_hdr' main/rfc1867.c
cp -p $SRC/php-$OLD/config.nice . # 前回と同じ条件でビルドする
./buildconf --force
./config.nice
make -j$(nproc)
make install
systemctl restart php-fpm # mod_php なら apachectl gracefulwgetを使うなら、wget -O php-$NEW-backports.tar.gzのように、保存するファイル名を指定しておくと安心です。
このあとは、1つずつ詳しく見ていきます。本番のサーバーなら、バックアップとインストール前の確認は省かないでください。
修正済みのCVEを確かめる
修正が入っているかは、ダウンロードして展開したソースの中身で確かめるのがいちばん手軽です。gitがなくても確かめられます。
# 展開したフォルダ(php-src-backports-$NEW)の中で
grep -nE 'rfc1867_enabled\) \|\| (true|1)\)' ext/session/session.c
grep -c 'php_mime_should_save_hdr' main/rfc1867.c1つ目で行が出て、2つ目が1以上なら、修正は入っています。どちらも出なければ、修正が入る前にダウンロードしたソースなので、ダウンロードし直しましょう。
2つのgrepで見ているのは、修正で書き換わった次の2か所です。
| ファイル | 何が変わったか | 探す文字 |
|---|---|---|
ext/session/session.c | アップロードの進み具合をセッションに書き込む機能を、まるごと止めた | 7.4〜8.1は|| true7.0〜7.3は || 1 |
main/rfc1867.c | アップロードのヘッダーの読み方を直した | php_mime_should_save_hdr |
1つ目は、バージョンによって書き方がちがいます。grep -Eで、どちらでも見つかるようにしました。
ダウンロードする前に知りたいなら、コミットの説明の全文で確かめる方法もあります。PHP2Shellの修正は「Avoid state corruption via bailout」というコミットで、説明の中にCVEの番号が書いてあります。
git log --onelineは1行目しか出さないので、CVEの番号では見つかりません。NEWSにも書かれていないので要注意です。
# コミットの説明(全文)に CVE の番号があるか
cd $SRC
git clone --filter=tree:0 --no-checkout --branch $NEW https://github.com/shivammathur/php-src-backports.git php-$NEW-check
git -C php-$NEW-check log -i --grep='CVE-2026-10961' --format='%h %cd %s' --date=shortGitHubのページで、ブラウザから確かめることもできます。
- 8.1のPHP2Shellの修正(Avoid state corruption via bailout):説明の中に、2つのCVEの番号が書いてあります
- 8.1のブランチのコミット一覧:ほかのバージョンは、URLの
8.1を8.0や7.4に変えると見られます - README:バージョンごとのブランチと、ダウンロードのリンクの一覧
直したいCVEの番号が出てこなければ、そのバージョンに上げても穴は残ります。ここで止めましょう。
config.niceを残す
# 前回ビルドしたフォルダの config.nice を残す
cp -p $SRC/php-$OLD/config.nice /root/php-$OLD-config.nice
# 今のモジュールの一覧(あとで比べる)
/usr/local/php/bin/php -m > /root/php-$OLD-modules.txt
# 今の PHP を丸ごと残す
cp -a /usr/local/php /usr/local/php-$OLD
cp -a /etc/php /etc/php-$OLD # --sysconfdir の場所に合わせるPHPは、configureするたびにconfig.niceというファイルを作ります。中身は、そのときのconfigureのコマンドそのもの。CFLAGSなどの環境変数も残るので、これをコピーして実行すれば、前回と同じ条件でビルドし直せます。めっちゃ便利です。
前回のビルドのフォルダを消してしまったときは、/usr/local/php/bin/php-config --configure-optionsで、オプションだけは取り出せます。
config.niceでビルドする
cd $SRC
curl -L -o php-$NEW-backports.tar.gz \
https://github.com/shivammathur/php-src-backports/archive/$NEW.tar.gz
tar xzf php-$NEW-backports.tar.gz
cd php-src-backports-$NEW
./buildconf --force # configure を作る
cp -p /root/php-$OLD-config.nice ./config.nice
./config.nice # 前回と同じ条件で configure
make -j$(nproc)インストール前に確かめる
sapi/cli/php -v # 新しいバージョンになっているか
sapi/cli/php -m | diff /root/php-$OLD-modules.txt - # モジュールが減っていないかdiffで<の付いた行が出たら、そのモジュールが新しいPHPに入っていません。configureのオプションを見直しましょう。
make installで入れ替える
make install
# PHP-FPM の場合
systemctl restart php-fpm
# mod_php の場合(LoadModule が重複して書き足されていないかも見る)
grep -n 'LoadModule.*php' /path/to/httpd.conf
apachectl configtest && apachectl gracefulphp.iniとphp-fpm.confは、make installでは上書きされません。同じ系列(8.1→8.1など)の中で上げるなら、peclで入れた拡張もそのまま使えます。
元のPHPに戻す方法
rm -rf /usr/local/php && cp -a /usr/local/php-$OLD /usr/local/php
systemctl restart php-fpm # mod_php なら apachectl gracefulビルドのエラーと対処法
実際にビルドしてみて、ハマったエラーは3つ。bisonが古い、ファイルの持ち主がちがう、OpenSSLが古い、です。
どれも、エラーの1行目を読めば原因の見当がつくものでした。出たエラーの文言に近い見出しを、このあとから探してみてください。
bison 3.0.0が必要と出る
configure: error: bison 3.0.0 is required to generate PHP parsers (excluded versions: none).下準備をしないでconfigureすると、こう言われます。php.netの配布ファイルには、bisonで作ったファイルが最初から入っているので、ふだんは気にしなくていいんですけどね。GitHubのソースならではのエラーです。
Permission deniedが出る
rm: cannot remove 'ext/openssl/xp_ssl.loT': Permission deniedconfigureをrootで、makeを一般ユーザーで……と混ぜると、こうなります。configure・make・make installのユーザーは、どちらかにそろえましょう。
OpenSSL 1.0.2で止まる
これはバックポートではなく、公式の8.2.35をCentOS 7でビルドしたときの話です。8.2.11は通ったのに、8.2.35ではext/openssl/xp_ssl.cで止まりました。
warning: implicit declaration of function 'ASN1_STRING_get0_data'
error: subscripted value is neither array nor pointer nor vectorASN1_STRING_get0_dataは、OpenSSL 1.1.0から入った関数。CentOS 7の標準のOpenSSLは1.0.2なので、この関数がないんです。
1.0.2には、同じ働きのASN1_STRING_dataがあります。そこで、次の3行を足して通しました。
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define ASN1_STRING_get0_data ASN1_STRING_data
#endif最初は手で書き足したら、#ifdefの中に入ってしまって「unterminated #ifdef」で怒られました。なんでやねん…。最後の#include <openssl/...>の行の下に、コマンドで差し込むのが安全です。
cp -p ext/openssl/xp_ssl.c ext/openssl/xp_ssl.c.orig
cat > /tmp/xp_ssl_compat.txt <<'EOF'
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define ASN1_STRING_get0_data ASN1_STRING_data
#endif
EOF
n=$(grep -n '^#include <openssl/' ext/openssl/xp_ssl.c | tail -1 | cut -d: -f1)
sed -i "${n}r /tmp/xp_ssl_compat.txt" ext/openssl/xp_ssl.c
diff -u ext/openssl/xp_ssl.c.orig ext/openssl/xp_ssl.c # 3行増えていればOKただ、これはソースを直接いじる応急処置。OpenSSL 1.0.2もCentOS 7も、すでにサポートが切れています。OSごと移る計画も、あわせて立てておきたいところです。
まとめ
- PHP2Shellの公式の修正版は8.2以降だけ。8.1以前は、開発チームの最小パッチか有志のバックポート
- まずは
session.upload_progress.enabled = 0で応急処置。ini_set()や.user.iniでは効かない - php-src-backportsを使うなら、直したいCVEが入っているかを先に確かめる
- GitHubのソースなので、bisonとre2cが必要。PATHはmakeのときも通しておく
- 前回のビルドの
config.niceをコピーすれば、同じ条件でビルドし直せる - バックポートはつなぎ。8.2も2026年12月31日でサポートが終わるので、本命は8.3・8.4への移行
IT系フリーランスエンジニアとしては、まず穴をふさいで時間を作る。その間に移行の段取りを組む。これが現実的な落としどころだと思います。
修正版の情報は、こちらにまとめています。
【追記:2026年10月22日】攻撃はもう始まっています。Amazonの脅威情報チームによると、修正版の公開から数時間で攻撃用のコードが出回り、「すでに広く悪用されている」とのこと。公表より4日前の10月17日にも、攻撃の試みが見つかっていました。22日にはIPAとJPCERT/CCも注意喚起を出...
攻撃の跡の探し方は、こちら。更新する前に入られていないか、あわせてチェックしておいてください。
PHPの脆弱性「PHP2Shell」(CVE-2026-109618/CVE-2026-109619)は、修正版の公開から数時間で、悪用が始まっています。Amazonによると、公表の4日前の10月17日には、すでに攻撃の試みがあったとのこと。 となると気になるのが、「更新する前に、もう入られ...

この記事へのコメントはありません。