WordPressのセキュリティ対策として、サイト全体を見た目そのままの静的ファイルに置き換えました。
WordPressだからダメなんじゃん?と思った
AI主導と思われる不正アクセスがすごい勢いで広がっているようです。うちは弱小サイトなので、狙われる対象にもならないと思うんですが、一応Wordpressは対策しておこうと思いました。
とはいえWordpressはPHPベースで動いていて、そのPHPも先日php2shellで脆弱性指摘があって、Wordpressそのものもセキュリティ対応アップデートがバンバンでてきます。
昔はmovabletypeという静的コンテンツを生成するブログプラットフォームがあったのですが(なんとPerlベース!!)それをあっという間に駆逐したのがWordpress。ところが動的生成できるおかげでセキュリティ的には脆弱性を露呈する事態に。
考えれば考えるほど「そもそもWordpressだからダメなんじゃん?」となりまして……。公開しているWordpressは、ログイン画面もPHPもプラグインも、攻撃の入口になります。けどそのログイン画面って、ただただ編集や管理のために使われるもので、それがオンラインにあるから脆弱性が出るわけです。
みてもらうのはそこじゃなくて生成された記事ファイルだけ。じゃぁ見た目はそのままで、全部のページを静的ファイルに置き換えてしまうのが一番セキュアなんじゃん?と思ったのでClaudeに相談してみたところ、Mac miniにDockerでWordpressを移設し、そこを編集用CMSとして利用、更新したら生成されたHTMLをリモートにミラー、という方法が出てきました。
検索ができなくなるとか色々デメリットもありますが、ただでさえAIでトラフィック減ってる中で、AIにクラックされるリスクともうバランスしないので、フル静的コンテンツ化を決意しました。
やることの全体像
もとのWordpressは、Mac miniのDockerにのせて、そこで普通に使います。記事を更新したら、差分の静的ファイルを生成して、リモート(サーバー)に反映する、という運用です。サーバー側のfreesim.tokyoは、HTMLを返すだけになります。
手元のWordpressをDockerで動かす
サーバーのDBとwp-contentをコピーして、Mac miniのDocker(Colima)にWordpress(PHP 8.3、MariaDB 10.11)を立ち上げました。URLは「freesim.tokyo」です。公開記事650件が、サーバーと同じようにそろっていることを確認しています。
手元のコピーでは、外部に通信するプラグイン(PushPress、Site Kit、Akismetなど)を止めて、メールの送信やピンバックも遮断しています。うっかり購読者に通知が飛んだりしないように、です。
静的ファイルを書き出す
書き出しはPythonで自作しました。手元のWordpressをクロールしてHTMLを取り、手元のURLを公開のURLに置き換えます。置き換えが漏れていないか(手元のURLが残っていないか)の検査もつけています。
- HTMLは3,105ページ、合計で約179MB、書き出しは1分ほど
- 画像(17,652ファイル、約811MB)は書き出さず、サーバーに差分だけ送る
- GA4とAdSenseのタグは、そのまま出力に入れる
同じ内容でも、書き出すたびに約540ページが変わります。関連記事がランダムで選ばれているからでした。1本公開すると数百ページが変わるのは、正常ということになります。
サーバーに反映する
サーバーには、rsyncで新しいフォルダに送って、「current」というリンクを付け替えて切り替えます。nginxの設定は、静的ファイルを返すものに切り替えるだけです。戻すときも、設定を元に戻すだけ。
切り替える前に、別の名前の入口を作って、サーバーの中から全ページと、ページが参照している画像8,491件がすべて200で返ることを確認しました。
切り替えたあとは、wp-login.php、wp-admin、xmlrpc.php、wp-jsonが404になります。攻撃の入口が閉じた、ということです。
落とした機能
- コメント投稿
- サイト内検索
- AMP
- プッシュ通知
- 古いURLのリダイレクト(古い記事のURLは404になります)
人気記事は、静的ファイルだと閲覧数が数えられないので、GA4から取ってきて更新する予定です。これまではこういうのが自動でできちゃうのがWordpressのいいところだったんですけどね……。そういう自己満よりも不正アクセスの方が今ウェイト高いわけです。
更新の流れ
記事の更新は、これまでと同じように手元のWordpressで書いて、ローカルで公開します。そのあとに書き出しと送信をするだけです。書き出しのあとに、ページ数が前回より大きく減っていないかを検査して、問題なければ送信します。
この記事から、この運用で更新しています。
実はこの記事もベースは作業ログとプロット指定で自動でドラフト作らせてから加筆してるので、記事の更新は加筆しながら知りましたw