プラグインなしから発想するWordPressセキュリティ – IP Geo Blockの更新告知

昨年末、一晩で15000余りの パスワード辞書によるクラッキング を受けました。その時のログを分析してみて改めて思ったのは、「これだけランダムで長けりゃ大丈夫」的なパスワードも、「漏れたら終わり」って事です。

「ランダムで長いパスワード」をアカウント毎に頻繁に変えるのは面倒なワケで、長い間変えずに使い続けと、漏洩時のリスクが極めて高くなるってことだと思います。

おっと、何も パスワードの定期変更は必要か の議論へ乱入しようというワケじゃありません。

IPアドレスのジオロケーション情報を元に海外からの投稿を弾くスパム対策プラグイン IP Geo Block に、wp-login.phpwp-admin/admin.phpxmlrpc.php へのアクセスを遮断するセキュリティ機能、さらにはログを記録する機能を追加したので、その告知が本記事の主題です 😉 。

IP Geo Blockの制作コンセプト

WordPress がセキュリティ的に最も強固なのは、「テーマはデフォルト、プラグインは使用せず」という素の状態である

何か目新しいことを言っているつもりはありません。サーバー設定、パーミション設定、強いパスワードなど、基本的な条件を整えた後 は、プラグインまみれの WordPress より、クリーン・インストール直後の WordPress の方が、脆弱性を抱える可能性が低いってことです。

ハッカーやクラッカーに、「なぜハッキングやクラッキングをするのか」と問えば、「そこに脆弱性があるから」と答えるかは分かりませんが(それぞれ目的が違いますからネ)、テーマを弄り、有象無象のプラグインを使い出す結果、脆弱性を抱え込むことになります。

そこでセキュリティ・プラグインの出番となるワケですが、単機能で物足りなかったり、iThemes SecurityBulletProof SecurityWordfence Security などの フルスペック なプラグインでは、多機能過ぎて設定が難しかったり重たかったりと、私的には「単機能以上、重量級未満」の ちょうどいいサイズ のプラグインがあってもイイんじゃないかと思ってました。

速度比較については後述しますが、例えば Wordfence Security では明らかにページの応答速度が悪化します(しかも128Mのメモリを要求する!)。

ということで、今回も自分が ちょうどいい と思う機能をだけを実装しました:D。

出来る限りシンプルかつ軽量に

ウチの様な共有サーバーでも問題なく使えるよう、従来の遮断機能 に加え、WordPress が標準で備えている幾つかの典型的な「入口」を遮断対象に追加しました。実際、コア部分は、コメントを含め60行弱の PHP を追加した程度です。

またシンプルな故、例えばIPアドレスの国別遮断機能がプレミアム版のみで提供される Wordfence Security と組み合わせるのも、有効だと思います。

ログ機能は手厚く

私が借りている LaCoocan では、月別の統計的アクセスレポートはあるものの、個別のアクセスログを見ることができません。そのため、いつ何処にどんな攻撃を受けたかを特定する手がかりが少なく、攻撃の事前対策や事後処理が困難です。

有名なプラグインに 狂骨 がありますが、ログする対象を xmlrpc.phpadmin-ajax.php にも拡大した感じと思ってもらえれば良いと思います。

オプションは極力少なく、拡張性は高く

誰かが使うかもしれない機能をオプションに組み込んで無闇にコードを肥大化させるより、各所にアクション・フックを設けて拡張性を残すことを選びました。またユースケース別サンプルコードを sample.php に載せ、同梱しています。

機能の概要

ログ機能

より詳しい情報が得られるよう、$_POST データのキーを指定すると、その中身を展開して記録する機能を持たせました。

$_POSTデータのキーを展開して記録

$_POSTデータのキーを展開して記録

ログは、カテゴリ毎に DB 上のリング・バッファ(最大100個で固定)に記録されます。また攻撃の事後に解析できるよう、選択的にバックアップ・ファイルを残すためのアクション・フックも設けました。

/**
 * パスワードが破られたか否かを後で確認できるようバックアップする
 *
 * @param  string $hook 'comment', 'xmlrpc', 'login', 'admin' のいずれか
 * @return string $path 非公開領域への絶対パス、または null(バックアップなし)
 */
function my_backup_dir( $hook ) {
    if ( 'login' === $hook )
        return '/absolute/path/to/';
    else
        return null;
}
add_filter( 'ip-geo-block-backup-dir', 'my_backup_dir' );

バックアップは CSV フォーマットのテキストファイルとして月別に記録されます。public_html などの公開領域以外で書き込み可能な場所を指定して下さい。

遮断機能

本プラグインでは、WordPress で最も基本的かつ重要な以下の4カテゴリ、7つの投稿・攻撃パターンを遮断対象としています。

wp-comments-post.php

2014年11月21日、WordPress コアに存在した XSSCSRFSSRF 脆弱性を修正する 4.0.1 セキュリティ・アップデート版がリリース されました。

この中には、ダッシュボード上で管理者が承認待ちのコメントを確認すると、管理者権限を攻撃者が取得できてしまうという、格納型(蓄積型あるいは持続的)XSS の修正が含まれています(出典1出典2)。

コメントの投稿とトラックバックはここで受けますが、余計なものは内部に取り込まないのが一番ということでしょう。

xmlrpc.php

実はコレがとってもヤバいんです。その名の通り XML による 遠隔命令を実行する手続き の入口なんですが、単にピンバック・スパムの温床になるだけじゃなく、ピンバックを悪用した DDoS 攻撃の踏み台 *1 に使われたり、wp-login.php と同様にクラッキングの対象になったり、また古くは ポートスキャン が出来たりなど、いっそ閉じておくのが当然のようなシロモノです。

*1: WordPress 3.9.2 でピンバックによる DDoS 攻撃関連の セキュリティ・アップデート が、また Akismet でも 関連するアップデート日本語記事)がリリースされています。

一方で私も使ってますが、WordPress.org 公式のモバイルアプリJetpack の一部機能には XML-RPC が必要で、公式サイトの FAQ では、XML-RPC が禁止されているサーバー用回避プラグイン が紹介されているぐらいです。

本プラグインでは XML-RPC の利便性を残すため、海外からのアクセスを弾くのは勿論、クラッキングと見なせるアクセスに対しては、wp-login.php と同様、国内外を問わず認証の試行回数を制限する機能を実装しました。

wp-login.php

パスワードのクラッキングは国内から受ける場合もあり得ます。そこで「国内外を問わず、認証失敗が一定回数(現状は5回で固定)を超えた場合、そのIPアドレスからの認証要求を一時的に禁止」します(禁止期間はIPアドレスのキャッシュ保持期間と連動で、デフォルトは60分です)。

同様の機能は Login LockDown が有名です。こちらはIPアドレスだけではなく、ユーザー・アカウント単位でも認証の試行回数が管理されていて、brute-force と reverse-brute-force の両攻撃を効果的に防げるよう工夫されています。

試行回数の管理方法は、IPアドレス単位/アカウント単位ともに一長一短ありますが、「串の数はそう多くはない」ので、IPアドレス単位で十分だと判断しました。

wp-admin/[admin.php | admin-ajax.php]

このカテゴリは、直接的攻撃に対するというより、サイトに何らかの脆弱性が存在した場合の間接的防御という位置づけです。例えば、XSS 脆弱性が原因のセッション・ハイジャックや、権限のチェックが十分でない脆弱なプラグインを経由したファイルの不正アップロードや盗用といった攻撃が想定できるでしょう。

最近の例では、Paid Memberships Pro の脆弱性 による任意ファイルの漏洩や、Slider Revolution の脆弱性 による wp-config.php のダウンロードとバックドアのアップロードがこれに当たります。

RevSliderの脆弱性を突いた攻撃

RevSliderの脆弱性を突いた攻撃

Paid Memberships Proの脆弱性を突いた攻撃

Paid Memberships Proの脆弱性を突いた攻撃

これらを防ぐには .htaccess によるIPアドレスの制限が最も効果的ですが、本プラグインならIPアドレスに依らず、次の拡張用コードで防ぐことが出来ます。

/**
 * admin-ajax.php 経由の不正なアクセスを遮断する
 *
 * @global array $_GET, $_POST リクエストされた値
 * @param  array $validate
 * @return array $validate['result'] 不正な場合に 'blocked'(遮断)を設定
 */
function my_protectives( $validate ) {
    if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
        // 保護するファイル名のリスト
        $protectives = array(
            'wp-config.php',
            '.htaccess',
            'passwd',
        );

        $req = array();
        $req += array_values( $_GET ) + array_values( $_POST );
        $req = strtolower( urldecode( implode( ' ', $req ) ) );

        foreach ( $protectives as $item ) {
            // リクエスト中にファイル名を発見したら遮断
            if ( strpos( $req, $item ) !== FALSE ) {
                $validate['result'] = 'blocked';
                break;
            }
        }
    }

    return $validate; // 引き続き国コードを検証するため 'passed' は設定しない
}
add_filter( 'ip-geo-block-admin', 'my_protectives' );

余談ですが、Codex の wp-config.php を安全にする にある .htaccess の設定は、HTTP でアクセス した時に 403 Permission Denied を返すか、中身が空の 200 OK を返すか(何も出力しない PHP が実行されるだけ)の違いにしかならず、先の様に内部に脆弱性がある場合の漏洩は防げませんので、念のため 😉 。

# 内部からの漏洩は防げない
<files wp-config.php>
order allow,deny
deny from all
</files>

また、Wordfence Security には Slider Revolution 専用 WAF が入っていて(先の拡張用コードの $protectivesrevslider_show_image が指定されている感じ)、脆弱性を突いたアクセスをすると次の様な画面が表示されます。

Slider Revolution の脆弱性を突いた攻撃を遮断する Wordfence Security の WAF

Slider Revolution の脆弱性を突いた攻撃を遮断する Wordfence Security の WAF

一方、iThemes Security(4.6.2)は、設定をアレコレ弄ってみましたが遮断できず、また BulletProof Security(.51.4)は、.htaccess によってローカルから Admin 領域へのアクセスが禁止されるため、確認できませんでした 😉 。

出来ない事

セキュリティ対策では、「何が出来るか」と共に「何が出来ないか」の想定が大事です。当然ですが、本プラグインは、フルスペック・プラグインが謳い文句にしている以下の様な機能は持っていません。

  1. 認証の強化
    • パスワード強度のチェック、認証プロセスの強化(二段階認証など)
  2. 不要なサービスの削除
    • admin変更、アカウント名隠蔽 *2、XML-RPC 無効化などの機能
  3. 脆弱性や脅威のスキャン、特定攻撃パターンの検出、遮断
    • XSS 脆弱性 *3CSRF 脆弱性、ファイルや DB の改ざん、ファイルの不正なアップロード(マルウェア、バックドア、ドットファイルなど)やダウンロード、SQL インジェクション等々を検出する機能、あるいは特定の攻撃パターンを遮断する WAF 機能
  4. 攻撃を検知した時の警告
    • 管理者メール送信、管理画面への警告表示、アカウント・ロックなど
  5. 攻撃に対する事前準備や事後処理
    • DB のバックアップやリストア、被害拡大を防ぐサイト閉鎖機能など

全て対応しようとすれば遅くなるのも仕方がない気がしますが、特にプラグインを数多く抱えていると(数じゃなく質の問題ですけどネ)、本プラグインのように「典型的な入口」を抑えるだけではダメで、3. 以降を重視せざるを得ないでしょう。

*2: 個人的には、隠すセキュリティ対策 はあまり重視していません。

*3: スキャンや WAF により、間接的に検出するんじゃないかと思いますが…。

応答時間の計測

どれだけ遅くなるかを見るために、ローカルに仕立てた WordPress で、ブルートフォース攻撃を想定したログイン・パスワードのクラックに対する処理能力を計測しました。

計測方法

WordPress brute-force attack detection plugins comparison を参考に、ApacheBench コマンド を使って計測しています。

# ab -t 60 -c 5 -p 'postdata.txt' -T 'application/x-www-form-urlencoded' -C 'wordpress_test_cookie=WP+Cookie+check' http://localhost/wordpress/wp-login.php

postdata.txt には、ログイン・フォームから送信される内容を入れておきます。

log=admin&pwd=bruteforce&wp-submit=Log+In&redirect_to=http%3A%2F%2Flocalhost%2Fwordpress%2Fwp-admin%2F&testcookie=1

計測結果

① セキュリティ・プラグインを有効にしていない場合、② IP Geo Block だけを有効にした場合、③(重量級プラグインの代表として)Wordfence Security だけを有効にした場合の3つについて計測しました。各プラグインの設定は、それぞれデフォルトのままです。

計測の環境と結果は以下の通りです。

  • プラットフォーム:MacBook Pro Retina, 13-inch, Late 2013
  • プロセッサ:2.8 GHz Intel Core i7
  • メモリ:16 GB 1600 MHz DDR3
  • OS:OS X 10.9.5
  • サーバー:Apache/2.2.22
  • PHP:5.4.30
  • WordPress:4.1-ja
プラグイン リクエスト/秒(平均)[#/sec]
① なし 11.28
② IP Geo Block (2.0.0) 11.76
③ Wordfence Security (5.3.4) 5.12

「プラグインなし」より「IP Geo Block」の方が若干向上していますが、これは WordPress の一連の処理のうち、比較的早い段階で弾いているからです。

一方 Wordfence Security では2倍ほどのコストがかかっています。キャッシュ・プラグインの併用など、何らかの高速化手段が必要でしょう(Wordfence Security は、それ自身がキャッシュ機能を持っています)。

今後は…

特に構想はありませんが、サンプルコードの一部を本体に取り込んでもいいかな?と思っています。

「単機能以上、重量級未満」にどれだけのニーズがあるか分かりませんが、ニッチはニッチらしく行きたいと思いますし、要望、バグ報告など大歓迎です 😀 。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

海外からの投稿は受け付けていません