
目次
ログイン名は、ログインしていない人にも見えています
WordPressのユーザー名(ログイン名)は、ログインしていない訪問者からも見えてしまうことをご存じでしょうか。設定を変えた覚えがなくても、標準のWordPressは次のような場所でログイン名、あるいはログイン名から作られた名前を出しています。
- REST APIのユーザー一覧(
/wp-json/wp/v2/users) - oEmbedの投稿者情報(
author_name/author_url) - ユーザーのサイトマップ(
wp-sitemap-users-1.xml) - コメントや投稿者ページのHTMLに付くクラス名(
comment-author-名前) /?author=1の転送先(/author/名前/)- ログイン画面の文言(「ユーザー名が未登録です」と「パスワードが違います」が別々に出る)
ログイン名が分かってしまうと、攻撃する側に残るのはパスワードを当てる作業だけです。総当たりの的が半分決まった状態になります。ログイン試行の回数制限や画像認証を入れていても、そもそも名前を渡さないに越したことはありません。
この記事では、これらの場所をひとつずつ塞ぐ自作WordPressプラグイン「ETBS Account Guard」を紹介します。WordPress公式プラグインディレクトリに登録しており、無料で利用できます。
ETBS Account Guard について
ETBS Account Guard は、サイトを管理するアカウントを守るためのプラグインです。大きく2つの機能があります。
1. ログイン名の保護
- REST APIのユーザー関連のルートを、ログインしていない訪問者には返さない(
_embedで埋め込まれる投稿者情報も含む) - oEmbedの投稿者名と投稿者URLを、サイト名とトップページのURLに置き換える
- ユーザーのサイトマップを出さない
- コメントと投稿者ページのクラス名からログイン名を外す(ID入りのクラス名は残す)
/?author=1をトップページへ転送する- 存在しないユーザー名とパスワードの誤りで、同じ文言を出す。パスワード再発行の画面も、登録の有無で見た目が変わらないようにする
- 投稿者ページを404にする(テーマ次第でリンクが切れるため、既定はOFF)
- 表示名またはニックネームがログイン名と同じユーザーを、設定画面に一覧で出す
項目ごとにON/OFFできます。有効化した時点で、投稿者ページ以外はすべてONです。
2. アクセス制限
- 権限ごと・ユーザーごとに、許可したIPアドレスからの接続だけを認める(IP制限)
- 同じく権限ごと・ユーザーごとに、WordPressのログインとは別のIDとパスワードを追加で求める(BASIC認証)
- 拒否した記録を直近100件まで管理画面に表示
- 設定を誤って誰も入れなくなることがないよう、締め出しにつながる保存は理由を添えて拒否する
- それでも入れなくなった場合に備えて、
wp-config.phpから止められる非常用スイッチ
インストール手順
- WordPressの管理画面で「プラグイン」→「新規追加」を開き、「ETBS Account Guard」で検索します
- 検索結果から「今すぐインストール」→「有効化」の順にクリックします
- 有効化した時点で、ログイン名の保護は投稿者ページ以外がONになります
- 管理画面左メニューの「設定」→「ETBS Account Guard」で内容を確認してください
使い方
1. ログイン名の保護
「設定」→「ETBS Account Guard」の「ログイン名の保護」タブで、項目ごとにON/OFFを切り替えます。それぞれ、OFFにするとWordPress本体そのままの動作に戻ります。
効いているかどうかは、ブラウザーのプライベートウィンドウで https://サイトのURL/wp-json/wp/v2/users を開けば確認できます。ONになっていれば、ユーザーの一覧は返ってきません。
2. 投稿者ページをOFFにしている理由
投稿者ページだけは既定がOFFです。テーマが投稿者名をリンクにしている場合、404にするとそのリンクが「ページが見つかりません」になってしまうためです。
ONにする前に、サイトの投稿を開いて投稿者名をクリックしてみてください。アドレスに /author/ を含むページが開くなら、そのテーマは投稿者ページへリンクしています。なお、ログイン中は引き続き表示されるので、確認はプライベートウィンドウで行ってください。
3. 表示名がログイン名と同じユーザーの一覧
上記の場所を塞いでも、表示名やニックネームがログイン名のままだと、記事の投稿者名やフィードにそのまま出てしまいます。同じタブに該当するユーザーの一覧を出しますので、各行からユーザー編集画面を開き、ニックネームと「ブログ上の表示名」を変えてください。自動では変更しません。
4. アクセス制限(IP制限)
「アクセス制限」タブで、権限ごと・ユーザーごとにIP制限を設定できます。administrator の権限そのものは常に制限なしで、特定の管理者だけを制限したい場合はそのユーザーの編集画面から設定します。
拒否の仕方は場面によって変えています。ログイン時は、パスワードの誤りと同じ文言で拒否します(理由を明かさないためです)。ログイン後の毎回のアクセスでは、そのセッションだけを破棄してログインしていない状態として続けます。エラー画面で止めないので、ログイン不要の公開ページやフォームは訪問者と同じように使えます。
IPの一覧は1行に1つ、IPv4・IPv6の単独アドレスかCIDRの範囲を書きます。#より後ろはメモとして扱われます。
5. アクセス制限(BASIC認証)
IP制限では絞れない環境(自宅や外出先など、接続元が変わる場合)向けに、BASIC認証も選べます。ユーザーごとに、WordPressのログインとは別のIDとパスワードを設定し、ブラウザー標準のサインイン画面で入力してもらう方式です。独自のログインフォームは作りません。
サーバーの構成によってはBASIC認証のヘッダーがWordPressまで届かないことがあるため、「受信の診断」を用意しています。診断が成功するまでBASIC認証モードは有効にできません。届かない場合の .htaccess の記述例は表示しますが、プラグインが自動で書き換えることはありません。
6. 非常用スイッチ
アクセス制限で誰も入れなくなった場合は、wp-config.php に次の1行を追加すると止められます。止まるのはアクセス制限だけで、ログイン名の保護は動作し続けます。
define( 'ACGD_DISABLE_RESTRICTION', true );
技術的な実装について
「権限エラーを返す」のではなく、ルートごと消す
REST APIのユーザー一覧を塞ぐ方法はいくつかありますが、権限チェックで弾いて403を返す作りにすると、実は目的を達成できません。存在するユーザーIDには「権限がありません」、存在しないIDには「見つかりません」と、応答が分かれてしまうことがあるからです。これでは、どのIDが実在するかを外から総当たりで調べられます。
そこで本プラグインでは、未ログインのリクエストに対してはルートの登録そのものを取り消しています。
public static function filter_rest_endpoints( $endpoints ) {
// 安いスイッチを先に見て、次に現在のユーザーを見る。
if ( ! is_array( $endpoints ) || ! self::is_enabled( 'rest_users' ) || is_user_logged_in() ) {
return $endpoints;
}
foreach ( array_keys( $endpoints ) as $route ) {
if ( self::is_users_route( $route ) ) {
unset( $endpoints[ $route ] );
}
}
return $endpoints;
}
ルートが存在しなければ、存在するIDも存在しないIDも同じ応答になります。「エラーの出し分けが情報を漏らす」というのは、ログイン画面の文言を揃えるのと同じ考え方です。なお is_user_logged_in() を見ているので、ブロックエディタなどログイン中の利用には影響しません。
締め出しを作る保存を、保存の時点で拒否する
アクセス制限のような機能で怖いのは、機能そのものより「設定を間違えて自分が入れなくなる」ことです。非常用スイッチは用意していますが、wp-config.php を編集できる人がいなければ詰みます。
そこで保存の前に、その設定を適用した場合に「制限されていない管理者(正確には、オプションを管理できるユーザー)」が何人残るかを数えています。
public static function count_unrestricted_admins( $role_modes, $forced_user_modes = array() ) {
$count = 0;
foreach ( self::get_manage_options_candidates() as $user ) {
if ( ! user_can( $user, 'manage_options' ) ) {
continue;
}
$forced = array_key_exists( $user->ID, $forced_user_modes ) ? $forced_user_modes[ $user->ID ] : null;
if ( empty( self::compute_effective_modes( $user, $role_modes, $forced ) ) ) {
++$count;
}
}
return $count;
}
引数の $forced_user_modes は「これから保存しようとしている値」です。保存済みの設定ではなく、保存した後にどうなるかで数えるのがポイントで、これがないと「今は大丈夫だから通す」という判定になってしまいます。数えた結果が0人になる保存は、理由を添えて拒否します。
同じ考え方で、「保存する本人が、今の接続元では満たせない設定」も拒否します。設定画面を開けている人が、保存ボタンを押した瞬間に締め出される、という事故を防ぐためです。
注意事項
- 二段階認証・ログイン試行の回数制限・画像認証・WAFは含みません。これらは専用のセキュリティプラグインをお使いください。
?author=の転送やログイン画面の文言は一部のプラグインと機能が重なりますが、両方入れても害はありません - SiteGuard WP Pluginと併用する場合は、SiteGuardの「ログイン詳細エラーメッセージの無効化」をONのままにしてください。OFFにすると、SiteGuard自身が出すエラーの文言で存在するアカウントが分かってしまい、こちらでは変えられません
- WooCommerceのマイアカウントにある「パスワード再発行」フォームは独自の文言を出すため、対象外です。ログインフォームは対象です
- テーマが出力する投稿者ページへのリンクには、URL用の名前がそのまま入ります
- パスワード再発行でメールを送れなかった場合は、そのエラーをそのまま表示します。サイトのメール送信の不具合に気付けるようにするためです。このエラーと、メールを送る場合と送らない場合で生じる応答時間の差は残ります
- ログイン画面では、WordPressがアカウントの存在するときだけパスワードを照合するため、応答時間に差が出ることがあります。これはまだ塞いでいません
ダウンロード
本プラグインは「ETBS Account Guard」の名前で WordPress 公式プラグインディレクトリに登録されています。管理画面の「プラグイン」→「新規追加」から検索してそのままインストールでき、更新も管理画面から受け取れます。
有償サポートやカスタマイズをご希望の方は、弊社サポートページ からお問い合わせください。
自己紹介
インディーズでミュージシャンをやっていたのですがいつのまにか…
とある企業でショップのアルバイトスタッフから正社員、支店長を経てシステム部門に異動するという、開発担当としては変わった経緯を持っている方だと思います。
「Excel VBA」からスタートして、Yamaha RTX シリーズで VPN環境構築、Hyper-V環境構築、Windowsアプリ開発などを経験した後、「 WordPress 」に出会い、どっぷりハマっています。
現在勤めているETBS合同会社では、「 WordPress 」を活用したWEBサイト、業務用WEBアプリケーション開発を中心に、記事の執筆代行や掲載に必要な情報のリサーチ、映像のテロップ入れや切りはりなどの簡単な動画編集なども、まとめて行なっています。
宮崎県 都城市 出身。東京都 葛飾区 在住。現在、代表兼二児のパパ。子育てを通じて、こどもたちにもプログラミングの楽しさに触れてほしいと思うようになり、「 こどもICTかつしか教室 」を開講中。最近は童心に帰り、簡単なゲーム制作なんかも楽しんでいます(^_^)。







コメントを残す