WordPressはお手軽なブログサイトを構築するのに本当に簡便だ。
なのだが、やはりWordPressの構造がとても分かりやすいため、攻撃者からもどこを突っつけばいいのか丸分かりである。

以前、WordPressのセキュリティを考える(超簡易版)というエントリーで触れたが、HTTPSとBASIC認証を組み合わせる事である程度楽にいい感じのセキュリティになったのではないかという方法を考えたので紹介する。

  • HTTPとHTTPSを同じドキュメントルートで公開する(例えば両方とも/var/www/html)
  • WordPress本体をサブディレクトリーに置く。(例えば/wpというサブディレクトリーに置くと/wp/wp-config.php)
  • /index.phpは/wp/index.phpをrequireする(直接/wp/wp-blog-header.phpをrequireしても良い)。
  • .htaccessでアクセス制限とrewriteを制御できる状態にしておく。

以上の状態で、/wp/.htaccessを置き、以下のような感じにする。BASIC認証のファイルパスなどは適当に。

<Files "wp-cron.php">
    Order deny,allow
    Deny from All
    Allow from xxx.xxx.xxx.xxx
    Satisfy Any
</Files>

<Files "*.php">
    SSLRequireSSL
    AuthType Basic
    AuthName "Welcome to WordPress Service"
    AuthUserFile /var/www/.html.htpasswd
    Require valid-user
</Files>

ErrorDocument 403 /.nodocument.nodocument

SSLRequireSSLにより、/wp/*.phpにHTTPアクセスしてきた場合はすぐさま403になる。HTTPSでアクセスして初めてBASIC認証がかかり、それを突破するとWordPressのログイン画面が表示される。wp-cron.phpだけ特別扱いで、これは特定のIPアドレスからアクセスして来た時は無条件で許すようにする。

以上の動きはBASIC認証とmod_rewriteでも対応できそうなものだが、mod_rewriteはBASIC認証の後で動作するので、wp-login.phpにHTTPでアクセスしてきたらHTTPSに転送→HTTPSでBASIC認証という事ができない。HTTPでBASIC認証した後で転送が行われてしまい、BASIC認証のパスワードが平文で流れるという問題に対応できないのだ。

さらに、ErrorDocumentの後の .nodocument.nodocumentは存在しえないPATH_INFOという意味で別にこの文字列に意味がある訳ではない。これを追記することで、HTTPで/wp/wp-login.phpにアクセスしてきた時に403が発生し、403により/.nodocument.nodocumentを返却しようとし、そのようなファイルは見つからないので404になる→WordPress本体によって404ページが表示される。

つまり、www.example.comというアドレスでWordPressサイトを立ち上げようとして、/wpにWordPress本体をおいた場合、分かる人が見ればhttp://www.example.com/wp/にWordPress本体があるのがバレてしまうが、実際にhttp://www.example.com/wp/wp-login.phpにアクセスしようとするとWordPressの404画面になるのだ。*.phpにしか制限をかけていないため、wp-includeの中に入っているJavaScriptやcssや画像はアクセスできる。

まとめると以上の設定を行う事によってwww.example.comは以下のような状態になる。

  • http://www.example.com/index.php WordPressによるコンテンツが返却される
  • http://www.example.com/wp/wp-admin/ 404
  • http://www.example.com/wp/wp-login.php 404
  • http://www.example.com/wp/wp-include/js/json2.min.js json2.min.jsが返却される
  • https://www.example.com/index.php WordPressによるコンテンツが返却される
  • https://www.example.com/wp/wp-admin/ 401
  • https://www.example.com/wp/wp-login.php BASIC認証→ログイン画面
  • https://www.example.com/wp/wp-include/js.json2.min.js json2.min.jsが返却される

HTTPSの/wp-admin/がなぜ401を返すか、それには理由があるがここでは解説しない。是非とも検討していただきたい。

さて、これで入口に関する設定は完了だが、まだWP-CRONに関して何もできていない。wp-config.phpに

define('DISABLE_WP_CRON', true);

というのを追記するとWordPressが自分の判断でwp-cron.phpをキックしようとするのをやめさせる事ができる。
そして、後はcronなど他の定期実行でhttp://<WordPressSiteURL>/wp/wp-cron.phpをどこかからかアクセスすれば良い。先ほど記載した.htaccessでxxx.xxx.xxx.xxxはこの定期実行を行うホストのグローバルIPアドレスを指定する。curlやwgetを定期実行する事になるだろう。