DEVLABはWordPressで運用しているのだが、そのWordPressについて気になる記事を見つけた。 WordPressサイトのホームURLに「?author=x」のGETクエリをつけることで、サイト内のユーザー名がばれてしまう というものだ(※ 詳しくはMT SystemsさんのWordPress TIPを参照)。?author=x
のxは数値で、WordPressのユーザーID(ユーザーテーブルのプライマリキー)となる。つまりxに1を指定すると、WordPressのルート管理ユーザーになるわけだ。 さっそく、DEVLABでも試してみたところ・・・ http://devlab.isao.co.jp?author=1
がみごとにパーマリンク設定で指定されているリライトルールに沿ってリライトされ、https://blog.colorkrew.com/author/<ルート管理ユーザー名>/
にリダイレクトされてしまった。このままだと、管理ユーザーのユーザー名に対してパスワードの総当りをされて、もしログイン成功とかされちゃうと、サイトがクラッキングされてしまうじゃないですか! ただ、DEVLABの場合、一般的なWordPressのログイン画面wp-login.php
は無効化してあって、ログイン方法を探すのが大変なので、一応は安全ではある。しかし、ユーザー名が漏洩してしまう脆弱性があるのは防止しないといけないので、対策を考えてみた次第。
MT Systemsさんのサイトで紹介されているように、著作者アーカイブページのテンプレートauthor.php
によってリダイレクトしてしまう方法ではHTTPのレスポンスヘッダにリダイレクトのlocationとしてユーザー名が出力されてしまうため、たとえ.htaccess
にrewriteルールを追加したとしても防止できないのが厄介だ。 MT Systemsさんのサイトでは、最終的にユーザーデータのuser_nicename
を書き換えて、ログインアカウントであるuser_login
と異なる値にしてしまう対処策が掲載されていたんだが、管理パネルから直接編集できない項目でもあって、なかなかに難儀である。
もうちょっと簡単に対策できないものか…。 ──と、言うわけで、対策方法を自作してみた。
// prevent the leakage of user name by author query on WordPress.
function knockout_author_query() {
// disable author rewrite rule
global $wp_rewrite;
$wp_rewrite->flush_rules();
$wp_rewrite->author_base = '';
$wp_rewrite->author_structure = '/';
// for author query request
if (isset($_REQUEST['author']) && !empty($_REQUEST['author'])) {
$user_info = get_userdata(intval($_REQUEST['author']));
if ($user_info && array_key_exists('administrator', $user_info->caps) && in_array('administrator', $user_info->roles)) {
wp_redirect(home_url());
exit;
} else {
// enable author rewrite rule
$wp_rewrite->author_base = 'author';
$wp_rewrite->author_structure = '/author/%author%/';
}
}
}
add_action('init', 'knockout_author_query');
上記のソースをテーマのfunction.php
などに追加することで即時有効になります。
処理の内容としては、パーマリンク設定でデフォルト以外のリライト設定をしているサイト用に、まずWP_Rewriteの処理を上書きしてauthorアーカイブへのリライトを一律TOPページ(ドメイン直下の’/’)へ変更している。次に、?author=x
のクエリを取得した時は、そのクエリで指定されているユーザーIDが管理者権限のユーザーであるならTOPページへ(パーマリンク設定をデフォルトから変更している場合はリライトし、デフォルトの場合はリダイレクト)、管理者以外のユーザーだった場合はauthorアーカイブを表示するようにした。 この処理の場合、一度author.php
テンプレート等でヘッダ送信後のリダイレクトではないので、リダイレクト(またはリライト)されてもHTTPレスポンスヘッダにはloctionが出力されないのだ。
?author=1でリクエストした際のHTTP リクエストヘッダー
GET /?author=1 HTTP/1.1
Host: devlab.isao.co.jp
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: ja,en-US;q=0.8,en;q=0.6
Cookie: (省略)
?author=1でリクエストした際のHTTPレスポンスヘッダー
HTTP/1.1 302 Found
Date: Fri, 12 Sep 2014 08:07:24 GMT
Server: Apache/2.4.9 (Win32) OpenSSL/1.0.1g PHP/5.5.11
X-Powered-By: PHP/5.5.11
Location: http://devlab.isao.co.jp
Content-Length: 0
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
しかしながら、これだけだとセキュリティ施策的には甘くて、管理者以外のユーザーでauthorアーカイブを表示するとこのユーザーのユーザー名が表示されてしまうので、結局のところ、ユーザー情報のuser_nicename
をuser_login
の値と異なるものにしておかないと、ログインアカウントを抜かれてパスワード総当り攻撃を喰らってしまうというリスクは残ってしまうのだ。ま、管理ユーザーのログインアカウントは漏洩しないので、最悪の被害にまではならないだろうが、サイト改ざんとかは余裕でやられてしまうので、authorアーカイブの機能を使いたい場合は、user_nicename
の変更も合わせて対策した方が良い。 authorアーカイブを利用しないサイトなら、先のソースからその部分の処理を削除して、下記のようにしてしまえば良い。
// prevent the leakage of user name by author query on WordPress.
function knockout_author_query() {
// disable author rewrite rule
global $wp_rewrite;
$wp_rewrite->flush_rules();
$wp_rewrite->author_base = '';
$wp_rewrite->author_structure = '/';
// for author query request
if (isset($_REQUEST['author']) && !empty($_REQUEST['author'])) {
$user_info = get_userdata(intval($_REQUEST['author']));
if ($user_info && array_key_exists('administrator', $user_info->caps) && in_array('administrator', $user_info->roles)) {
wp_redirect(home_url());
exit;
}
}
}
add_action('init', 'knockout_author_query');
この場合、どのユーザーだろうがauthorアーカイブは表示されずに一律TOPページにリダイレクトされるので、?author=x
クエリによるユーザー名漏洩の危険性はなくなる。DEVLABはこれで対策をしている。
さらに、パーマリンク設定でリライトを有効化しているなら、/author/
のパスへのアクセスを無効化しておくことでさらにセキュリティが上がるので、こちら(下記のリライトルールを.htaccess
に追加する)も対策しておくことをお奨めする。
RewriteRule ^author/(.*)? / [R=302,L]