コア更新の影響調査のつもりで functions.php を開いたら、バックドアが出てきた話
結論から。 WordPress のコア更新前の互換性調査で functions.php を通しで読んだところ、特定の GET パラメータを付けるだけで誰でも管理者になれるバックドアが数行、自作コードに紛れて入っていました。その場でコードを除去し、全セッションの無効化、管理者アカウントの確認、パスワード更新、アクセスログの確認まで行いました。長く運用しているサイトほど、コア更新は functions.php を人が読み直す数少ない機会です。
WordPress のコア更新にあたって、「カスタム投稿タイプのブロックエディタ制御コードが動かなくなるか見てほしい」という依頼を受けました。よくある互換性調査です。その調査の途中で、未認証のバックドアが見つかりました。
functions.php に何が入っていたのか?
functions.php の後半、自作の関数がいくつか並んだ後ろに、見慣れない数行が挟まっていました。要約すると、特定の GET パラメータを付けてアクセスするだけで、管理者ユーザーとしてログイン状態にするという内容です。
パスワードもトークンも要りません。URL を知っていれば誰でも管理者になれます。
インデントも書き方も周囲のコードに寄せてあり、ざっと眺めただけでは自作の処理に見えます。ファイル更新日時も、直近の作業日と大きくは違いませんでした。
バックドアを見つけたら、最初に何をする?
入口を閉じることです。侵入の経路調査より先に、まず入口を閉じます。順番はこうでした。
- 該当コードを除去
- 全ユーザーのセッションを無効化(ログイン中のセッションを強制的に切る)
- 管理者権限を持つアカウントの一覧を確認し、身に覚えのないユーザーがいないかチェック
- 残っている管理者アカウントのパスワードを更新
- アクセスログを遡り、当該パラメータ付きのリクエストが来ていないか確認
3番で身に覚えのないアカウントが増えているケースは珍しくありません。今回はありませんでした。
依頼範囲との関係
厳密に言えば、これは受けた依頼の範囲外です。ただ、見つけてしまった以上は報告しないという選択肢がありません。状況を説明したうえで即時対応の可否を確認し、その場で作業しました。
こういうときに「範囲外なので次回見積もりします」と返すと、その数日のあいだ穴が開いたままになります。止血だけ先にやって、原因調査と再発防止はあらためて見積もる、という分け方が現実的だと思っています。
コア更新のほうの話
本題だった互換性調査のほうも書いておきます。Block Hooks 関連の処理が REST コントローラ側へ移っていたため、旧来の書き方をしていたフックが期待通りに動かなくなる箇所がありました。あわせて register_post_type_args での上書きと競合していた古いコードを整理しています。
教訓めいたこと
コア更新の前に functions.php を通しで読む、という作業には副産物があります。互換性の確認だけが目的でも、そのファイルに何が書かれているかを人間が一度読む機会自体が、意外と少ないからです。
長く運用しているサイトほど、誰がいつ書いたか分からないコードが溜まっています。更新のタイミングは、それを棚卸しする数少ない口実でもあります。
まとめ
- バックドアは
functions.phpの自作関数の後ろに数行。書き方も更新日時も周囲に寄せてあり、ざっと見では分からない - 見つけたら順番は「コード除去 → 全セッション無効化 → 管理者アカウント確認 → パスワード更新 → アクセスログ確認」
- 依頼の範囲外でも止血だけは先にやり、原因調査と再発防止はあらためて見積もる
- コア更新の前に
functions.phpを人が通しで読む。それ自体が棚卸しになる
ほかの記事