2026年10月に公開されたPHPの緊急脆弱性php2shellは、修正版が8.2以上にしか出ていません。8.1以前にはPHP公式の修正版がないので、7.4で動いているサイトは、PHPそのものを上げるしかありません。
そこで、ロリポップで動いている、数年前に作られたWordPressのサイトのPHPを7.4から8.3に上げることになりました。
管理画面の「変更」ボタンを押せば、一瞬で切り替わります。ただ、押す前に「今の状態で上げたら、何か壊れるのか」を確かめておきたいところです。この記事では、そのときに確かめた順番と、実際に見つかった落とし穴をまとめます。
この記事の内容は2026年10月時点のものです。クライアントのサイトが特定できないよう、フォルダやファイルの名前は置き換えています。
php2shellそのものへの対応は、PHPの緊急脆弱性に、Xserver上のWordPressで一晩対応した記録 にまとめています。
サイトの構成
前提として、少し変わった構成のサイトでした。
- WordPressには トップページがない。CPT UIで作ったカスタム投稿(ブランド一覧、ニュース、スライドなど)を、データの置き場として使っている
- 表に出ているのはmakeshopで作ったECサイト。そのページが、WordPress側のサーバーからJS・CSS・画像を読み込んで表示している
- WordPressはサブディレクトリに置かれていて、サイトの直下にも自作のPHPが置かれている
この構成のせいで、「テーマだけ見れば大丈夫」とはいきませんでした。
手順1:テーマのPHPを確かめる
最初に、WordPressのテーマのフォルダ(16ファイル)を確かめました。
- PHP 8.3で構文をチェックする(
php -l) - WordPressの関数を空のダミーの関数に置き換えて
functions.phpを読み込み、警告が出ないか確かめる - 古い書き方を
grepで洗い出す(ereg、each()、create_function、$str{0}、引数の数の間違いなど)
for f in $(find . -name '*.php'); do php -l "$f"; done
結果は次のとおりです。
- 構文のエラーは0件。2000年代の文字コード変換のライブラリ(jcode)も、
eregを使っている関数が呼ばれていなかったので問題ありませんでした - 1か所だけ、PHP 8では処理が止まる書き方がありました
$w = intval(htmlspecialchars($_GET["w"]), ENT_QUOTES, "utf-8");
htmlspecialchars()に渡すはずの引数が、intval()の側に入ってしまっています。intval()は引数を2つまでしか受け取りません。
- PHP 7.4では警告が出て、戻り値が
nullになる(値はすでに壊れている) - PHP 8では
ArgumentCountErrorになって、処理がそこで止まる
今回はこの関数がどこからも呼ばれていなかったので、影響はありませんでした。直すならintval($_GET["w"])で十分です。
ほかには、falseの変数に配列として代入する書き方で、PHP 8.1以降にDeprecated(非推奨) の警告が出ました。動作には影響しません。
手順2:WordPress本体とプラグインのバージョンを確かめる
PHPを上げて壊れやすいのは、テーマよりもWordPress本体とプラグインです。管理画面の「ツール → サイトヘルス → 情報」と、プラグインの一覧で確かめました。
| 対象 | 判定 |
|---|---|
| WordPress 7.1.3 | 最新 |
| ACF / CPT UI / Advanced Editor Tools / Intuitive Custom Post Order / SiteGuard / WP Multibyte Patch / WP Simple Booking Calendar | 現行版 |
| Instagram Feed Pro Business 2.4.1(最新は6.x) | 何世代も古い有料版 |
古いInstagram Feedが心配でしたが、このサイトではすでに使われていなかったので、対象から外しました。
手順3:「どこからPHPが呼ばれているか」を調べる
ここが本題です。テーマとプラグインに問題がなさそうでも、まだ安心できませんでした。makeshop側が実際に何を読み込んでいるのかを確かめていなかったからです。
そこで、ECサイトをブラウザで開いて、DevToolsのコンソールで、読み込まれているファイルを一覧にしました。
performance.getEntriesByType('resource')
.map(r => r.name)
.filter(n => /php|json|wp/.test(n));
すると、次のファイルが見つかりました(名前は置き換えています)。
/assets/api/data.php:<script>として読み込まれるPHP。カテゴリ、ブランド、スライド、ニュースなどを、JSの変数として出力している/assets/css/base.cssなどのCSS:実は本物のCSSファイルではなく、.htaccessのリライトでPHPが返している
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ scss/index.php?url=$1 [QSA,L]
どちらも テーマのフォルダの外にあるファイルです。テーマだけを見ていたら、確実に見落としていました。
FTPで「PHPが動くもの」を全部回収する
ロリポップのPHPの設定は、そのドメインの下にあるすべての.phpに効きます。そこで、サイトの直下から、PHPがありそうなフォルダをまとめてダウンロードしました。
- 回収したもの:サイト直下の自作のフォルダ(
/assets/など)、WordPressのフォルダ直下のファイル、各フォルダの.htaccess - 回収しなかったもの:
wp-config.phpと.htpasswd(パスワードが入っているので要らない)、wp-admin/とwp-includes/(WordPress本体)
WordPressのフォルダ直下のファイルは、WordPressの公式のファイル一覧と照らし合わせて、自作のファイルが紛れていないかを確かめました(紛れていませんでした)。
手順4:見つかった自作のPHPを、PHP 8.3で動かす
| ファイル(名前は置き換え) | 役割 | 結果 |
|---|---|---|
| データをJSで出力するPHP(2つ) | makeshop用のデータをJSの変数として出力 | 警告なし |
| ブランド一覧のページ | ブランドの一覧ページ | 警告なし |
| 管理画面用のAPI | 管理画面のチェックリストを保存 | 問題なし |
CSSを返すPHPとscss.inc.php |
2013年版のscssphpでCSSを生成 | 動くが、Deprecatedが大量に出る |
古いSCSSのコンパイラは、本当に動くのか
いちばん心配だったのが、2013年版のscssphpです。そこで、PHP 8.3で3つのSCSSを実際にコンパイルして、今サーバーに置かれているCSSとバイト単位で比べました。
php -r 'require "scss.inc.php"; echo (new scssc())->compile(file_get_contents("base.scss"));' > base.css
cmp base.css ../compile/base.css && echo "完全一致"
3つとも 完全に一致 しました。ただし、次のような警告は出ます。
Deprecated: Using ${var} in strings is deprecated, use {$var} instead
Deprecated: Creation of dynamic property scssc::$env is deprecated
落とし穴:display_errorsが1のままPHPを上げると、表示が消える
自作のPHPの先頭には、開発中の名残でこの1行が残っていました。
ini_set('display_errors', 1);
PHP 8.3にすると、scss.inc.phpは 読み込まれるたびに Deprecatedの警告を出します。display_errorsが1のままだと、その警告の文が 出力(=JSやCSSの中身)にそのまま混ざります。
- データを出力するPHPはJSとして読み込まれているので、先頭に
Deprecated: ...が1行入るだけでJS全体が構文エラーになる - その結果、ECサイトのブランド一覧、ニュース、スライドなどが まとめて表示されなくなる
処理が止まるエラーではないので、構文のチェックでは見つかりません。それなのに画面からは消えてしまう、いちばんいやなタイプの問題です。
対策
対象の4ファイルを、すべて0にしました。
ini_set('display_errors', 0);
.htaccessには#php_flag display_errors 1という行も残っていました。コメントアウトされているので、そのままにしています。php_flagが.htaccessで使えるかどうかはサーバーのPHPの動かし方によって違い、使えない環境でコメントを外すと500エラーになることがあります。古い設定を不用意に戻さないのが安全です。
変更が本当に1行だけかを確かめる
本番に上げる前に、編集前のファイルと比べて次の点を確かめました。
diffで、変わっているのがdisplay_errorsの行だけであること- 先頭にBOMが付いておらず、改行コードもLFのままであること(BOMが付くと、
header()が効かなくなる) - PHP 8.3ですべての警告を出す設定にして動かしても、JSとCSSの出力に警告が混ざらないこと
本番に反映した手順
- 本番の4ファイルを、FTPで
backup/日付_php74/にダウンロードする(サーバーと同じフォルダ構成で) - 今のPHPの設定(
7.4(LiteSpeed版))を控えておく - 書き換えた4ファイルをアップロードし、PHP 7.4のままで表示が変わらないことを確かめる
- ロリポップのPHPの設定を
8.3(LiteSpeed版)に変える - ECサイトの各パーツ(ブランド一覧、ピックアップ、スライド、ニュース、サイドメニュー、カレンダー、CSS)を、スーパーリロードで確かめる
問題が出たときは、PHPを7.4に戻せば、すぐに元どおりになります。それでも直らなければ、backup/のファイルをアップロードし直します。
まとめ:PHPのバージョンを上げる前のチェックリスト
- テーマのPHPを、上げる先のバージョンで
php -lする - 引数の数、
each()、ereg、create_function、$str{0}など、PHP 8で処理が止まる書き方を洗い出す - WordPress本体とプラグインのバージョンを確かめ、古いものは先に更新するか外す
- フロント側が実際に読み込んでいるPHPを、ブラウザの読み込み一覧から洗い出す(テーマの外にあることが多い)
.htaccessのリライトで、PHPがCSSやJSのふりをしていないか確かめる- ドメインの下にある
.phpをすべて回収して確かめる(wp-config.phpなどの秘密の情報は除く) - JSやCSSを出力するPHPに、
display_errors = 1が残っていないか確かめる - バックアップを取り、ファイルの修正 → 確認 → PHPの切り替え → 確認 の順に進める
古いサイトほど、「PHPがどこで動いているか」がテーマの外に散らばっています。構文のチェックが通っても、警告の文が出力に混ざるだけで表示が消えることがあるので、JSやCSSを出力しているPHPには特に気をつけてください。
