Nginx+WorPressでBasic認証を追加する

認証

前回はこのサイトが不正ログインの攻撃を受けたため不安定になったことを紹介しました。

今回はその対策としてBasic認証を設定します。

Basic認証とは?

Basic認証とはある特定のWebページにアクセスしたときに、ユーザ名とパスワードを入力することを要求する仕組みです。

古い方法なのでいろいろ問題もあるのですが、今回のWordPressログイン連続攻撃は力技なので、Basic認証を設定することでサイトが落ちることを回避することができます。

なぜBasic認証が有効なのか?

今回問題だったのはWordPressのログイン画面(wp-login.php)に高い頻度でアクセスが発生し、そのたびごとにPHP5-FPMが動作してしまい、CPUを消費してしまっていることでした。

ログイン失敗すると一定時間ログインを受け付けなくする「Login Security Solutionプラグイン」などでセキュリティを高めても、PHP5-FPMが動作する回数を抑えなくては、サイトの不安定は解消できません。

一方、Basic認証はWebサーバ(Nginx)が行います。Basic認証を通過したあとに、初めてwp-login.phpが実行されます(PHP5-FPMが動作する)。このため、Basic認証を設けることで、サイトが不安定になることを防ぐことができます。

もちろんWebサーバ(Nginx)には負荷がかかりますが、簡単なBasic認証という方式であることと、Nginxが高負荷に強いWebサーバであることから、問題ないと判断しました。

Nginxの設定

NginxでBasic認証を設定する方法はGoogleで検索すると多数出てきます。

一見、非常に簡単なようなのですが、私のサイトは

の2点から、ちょっと工夫が必要でした。

なお、私のnginxの設定ファイルは下記で公開しています。

WordPress高速化: 最適化後のnginxの設定一覧
今回はWordPressを高速化するための設定変更を含めたnginxの設定を紹介します。 正直、正しいのかどうかわからない点も有りますが、WordPressとnginxを組み合わせようという方の参考になれば幸いです。

おさらい: ログイン画面/管理画面をSSL対応にする

nginxの設定を変える前にWordPressの設定を見直します。

見直す点はログイン画面と管理画面のセキュリティです。これらの画面では重要な情報をやり取りするので通常のhttpではなくhttps(SSL)で通信するようにしておいたほうが安全です。

https(SSL)を使うためにはWordPressの管理ファイル(wp-config.php)を変更します。詳しくは下記のページを参照してください。

以前はwp-config.phpだけhttpsでアクセスするようにしていたのですが、今回からは管理画面全体をhttpsでアクセスするように変更しました(「define(‘FORCE_SSL_ADMIN’, true)」を追記した)。

これで普通のhttpでWordPressのログイン画面や管理画面にアクセスしても強制的にhttps(SSL)での接続となります。

httpでアクセスするサイトの設定

まず、通常のhttp(ポート80)でのアクセスを受け付けるの設定ファイル(私の場合は/etc/nginx/sites-available/scratchpad-blog)についてです。

このファイルでWordPress関係の処理を記載しているので、このファイルBasic認証のための設定を記載すればよさそうですが・・・実は違います。

上述のようにWordPressの設定でログイン画面・管理画面を強制的にhttps(SSL)で接続するようにしているため、ログイン画面・管理画面に関する処理はこの設定ファイルではなく、httpsでアクセスするサイトの設定ファイルに従うのです。

したがって、特に変更する必要はありません。以前と若干記載を変えたので、再掲しておきます。

### http://scratchpad.jp の設定

# nginxのproxy cacheの設定
proxy_cache_path  /var/cache/nginx/scratchpad.jp levels=1:2 keys_zone=scratchpad.jp:64m inactive=1d max_size=768m;
proxy_cache scratchpad.jp;
proxy_cache_valid 200 302 60m; # 正常時のキャッシュ有効時間は60分
##proxy_cache_valid 404 10m;     # 404エラーのキャッシュ有効時間は10分
include proxy_params; # 共通設定は/etc/nginx/proxy_paramsに記載

# フロントエンドの設定
server {
    # scratchpad.jpに対するポート80への接続を処理する
    listen 80;
    server_name scratchpad.jp *.scratchpad.jp;

    root /www/scratchpad.jp;
    index index.php;

    # ログファイルの設定 エラーは警告レベル以上を記録
    access_log /var/log/nginx/scratchpad-front-access.log combined;
    error_log  /var/log/nginx/scratchpad-front-error.log warn;

    # proxy cacheを有効にする
    include global/wordpress-proxy-cache.conf;
}

httpsでアクセスするサイトの設定

次にhttps(SSL)でアクセスするサイト側を設定します。

上述したようにWordPressのログイン画面と管理画面をhttps(SSL)でアクセスするので、Basic認証の設定はこちらのファイルで行います。

また、https(SSL)で通常公開しているサイトにアクセスがあった場合は、強制的にhttpで公開するようにします。例えば、https://scratchpad.jp/にアクセスがあった場合はhttp:/scratchpad.jp/で公開するようにします。

これはhttpでアクセスした場合は、nginxのプロキシ・キャッシュ機能が効き効率が良いことと、外部からのリンクがhttpsとhttpで分散しないようにhttpで統一するためです。

上記を考慮してhttpsでアクセスするサイトの設定ファイル(私の場合は/etc/nginx/site-available/scratchpad-blog-ssl)を修正します。

# httpsでの接続時の設定 (WordPressの管理者用画面など)

server {
    # scratchpad.jpに対するポート443への接続を処理する
    listen 443;
    server_name scratchpad.jp;

    root /www/scratchpad.jp;
    index index.php;

    # ログファイルの設定 エラーは警告レベル以上を記録
    access_log /var/log/nginx/scratchpad-ssl-access.log combined;
    error_log  /var/log/nginx/scratchpad-ssl-error.log warn;

    # SSLを有効にする
    ssl on;
    ssl_certificate     /etc/ssl/certs/scratchpad.jp.crt;
    ssl_certificate_key /etc/ssl/private/scratchpad.jp.key;

    # 安全のためBasic認証
    location ~* /wp-login\.php|/wp-admin/(^admin-ajax\.php) {
        auth_basic      "Administrator Login";
        auth_basic_user_file "/etc/nginx/.htpasswd";

        # Basic認証通過したら普通にPHPを呼び出し
        include global/exec_php.conf;
        break;
    }

    ## basic認証不要な領域
    # 通常のページはhttpにリダイレクト
    location ~* ^/$|^/index|^/20[0-9][0-9]/|^/summary/|^/category/|^/tag/|^/about/ {
        rewrite ^(.*) http://scratchpad.jp$1 permanent;
        break;
    }

    # 共通の制限事項を設定
    include global/restrictions.conf;

    # マルチサイト用WordPressの設定を有効にする
    include global/wordpress-ms.conf;
}

20~28行目の記述がBasic認証のための記述です。アクセスしたURLにwp-login.phpかwp-admin/admin-ajax.phpが含まれているときにはBasic認証を要求します。

Basic認証のためのユーザ名とパスワードを記載したファイルは「/etc/nginx/.htpasswd」となります(作り方は後述)。

26行目は重要です。20行目で指定した条件で引っかかったアクセス(wp-login.phpとwp-admin関連)をどのように処理するかを記載します。ここでは処理内容は別ファイルのexec_php.conf(後述)に記載して、それを読み込むことにしています。このように処理内容を記述しておかないと、wp-login.phpにアクセスしてBasic認証に成功しても、ブラウザがwp-login.phpファイルをダウンロードしてしまい、期待した動作になりません。

ここでインクルードしているexec_php.confは

# ファイルが存在しない場合は404エラー
# 参照: http://forum.nginx.org/read.php?2,88845,page=3
try_files $uri =404;

fastcgi_split_path_info ^(.+\.php)(/.+)$;

fastcgi_send_timeout  5m;
fastcgi_read_timeout 5m;
fastcgi_connect_timeout 5m;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php_backend;

# Nginx Cache Controllerプラグインに必要
fastcgi_pass_header "X-Accel-Expires";

となっています。

30~35行目は、httpsでのアクセスをhttpに書き換える処理です。URLのホスト名以降が37行目の条件式にマッチした場合には強引にhttpでアクセスするようにリダイレクトします。ここでは次の条件のいずれかを満たした場合にこの条件式にマッチします。

  • 「^/$」: 「/」だけ。すなわちhttp://scratchpad.jp/が該当。
  • 「^/index」:「/index」で始まる場合。http://scratchpad.jp/index.phpやhttp://scratchpad.jp/index.htmlが該当。
  • 「^/20[0-9][0-9]/」:「/2013」など数字4桁で始まる場合。本ブログの各記事のパーマリンクが該当。
  • 「^/summary/」:「/summary/」で始まる場合。http://scratchpad.jp/summary/が該当。
  • 「^/category/」:「/category/」で始まる場合。http://scratchpad.jp/category/が該当。
  • 「^/tag/」:「/tag/」で始まる場合。http://scratchpad.jp/tag/が該当。
  • 「^/about/」:「/about/」で始まる場合。http://scratchpad.jp/about/が該当。

なおrestrictions.confとwordpress-ms.confについては下記を参照してください。

WordPress高速化: 最適化後のnginxの設定一覧
今回はWordPressを高速化するための設定変更を含めたnginxの設定を紹介します。 正直、正しいのかどうかわからない点も有りますが、WordPressとnginxを組み合わせようという方の参考になれば幸いです。

認証ファイル(.htpasswd)の作成

Basic認証の設定ファイルはhtpasswdコマンドで作成できます。このコマンドはapache2-utilsパッケージに含まれますので、まずはダウンロードします。

apt-get install apache2-utils

そして次のコマンドを実行して、設定したいパスワードを2回入力すると.htpasswdファイルができます。

htpasswd –c /etc/nginx/.htpasswd ユーザ名

動作確認

それでは動作確認をします。まずはnginxを再起動します。

service nginx restart

続いてログイン画面にアクセスします。httpsになっているか確認するために、あえて通常のhttpでログイン画面(http://ブログのURL/wp-login.php)にアクセスしてみましょう。

WordPressの設定ファイルが正しく設定されていれば、自動的にhttps://ブログのURL/wp-login.phpに飛ばされるはずです。なお、サーバの証明書が自己証明(オレオレ証明)である場合は、httpsでアクセスした際に証明書に関する警告が表示されます。

そして次のようなユーザ名とパスワードを入力するためのダイアログボックスが表示されればひとまずOKです。

Basic認証

ここでhtpasswdコマンドで指定したユーザ名とパスワードを入力してちゃんとWordPressの管理画面(ダッシュボード)が表示されれば完了です。

ブラウザがwp-login.phpというファイルをダウンロードしてしまったら、上記のscratchpad-blog-sslの26行目に相当する記述(PHP-FPMを実行するための記述)があるかどうか確認してください。

ログの確認

最後にログを確認してみます。設定ファイルのaccess_logで指定したファイルを確認してみましょう。

ログインに成功すると次のようなログが残っているはずです。aaa.bbb.ccc.dddは自分のIPアドレスです。

1行目がwp-login.phpにアクセスした際のログで、2行目がユーザ名/パスワードを入力してBasic認証に成功した際のログです。

aaa.bbb.ccc.ddd - - [07/Sep/2013:15:08:42 +0900] "GET /wp-login.php HTTP/1.1" 401 596 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36"
aaa.bbb.ccc.ddd - ユーザ名 [07/Sep/2013:15:08:55 +0900] "GET /wp-login.php HTTP/1.1" 200 1219 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36"

またerror_logで指定したファイルをしばらく監視してみましょう。時々次のようなログがあるはずです。

YYYY/MM/DD HH:MM:SS [error] 46811#0: *31503 no user/password was provided for basic authentication, client: eee.fff.ggg.hhh, server: scratchpad.jp, request: "GET /wp-login.php HTTP/1.1", host: "scratchpad.jp"

このログはIPアドレスeee.fff.ggg.hhhのユーザがwp-login.phpにアクセスしたものの、Basic認証に失敗したことを(=wp-login.phpに対する不正アクセスを事前にブロックしたこと)を示しています。

まとめ

今回はWordPressのログイン画面と管理画面にBasic認証を設定して、wp-login.phpに対する連続不正ログイン攻撃を防ぐ方法を紹介しました。

簡易的な方法ではありますが、現在のwp-login.phpへの攻撃を食い止めるには絶大な効果がありますので、設定しておいて損はないと思います。

Basic認証を追加した後は、私のサイトでは急激にロードアベレージが上がるということはなくなりました。

次回はWordPressで発生した問題を引き続き紹介します。