2026年7月5日

2026年7月5日

WordPressのログイン失敗をメールで通知する方法|wp_login_failedフックで作る

WordPress のログイン画面(wp-login.php)には、公開直後から総当たりのログイン試行が届くことがよくあります。試行が続いていることに早く気づければ、パスワードの見直しやアクセス制限などの対策を取れます。

この記事では、WordPress の wp_login_failed フックを使って、同じ IP アドレスからのログイン失敗が一定回数続いたら管理者にメールで知らせる仕組みを作ります。あわせて、管理者がログインしたときの通知と、プラグインで済ませる方法も説明します。コードは 2026年10月時点の WordPress 6.9 で動作を確認しています。

仕組み

WordPress はログインに失敗すると wp_login_failed アクションを実行します。

do_action( 'wp_login_failed', $username, $error );
引数 内容
$username 入力されたユーザー名またはメールアドレス
$error 失敗の理由を持つ WP_Error(WordPress 5.4 で追加)

失敗の理由はエラーコードで分かります。たとえば、ユーザー名は存在するがパスワードが違う場合は incorrect_password、存在しないユーザー名の場合は invalid_username です。ユーザー名かパスワードが空のとき(empty_username・empty_password)は、このフックは実行されません。

このフックは wp_authenticate() の中で実行されるため、ログイン画面のほか XML-RPC(xmlrpc.php)経由の認証失敗でも実行されます。一方、REST API のアプリケーションパスワードによる認証の失敗は別のフック(application_password_failed_authentication)で扱われ、wp_login_failed は実行されません。

ログインに失敗するたびにメールを送ると、総当たり攻撃を受けたときに数百通のメールが届いてしまいます。そこで次のように作ります。

  • IP アドレスごとに失敗回数を数える(WordPress の transient に保存)
  • 失敗回数が 5 回に達したら 1 通だけ送る
  • 同じ IP アドレスについては 1 時間は再送しない

手順1: ログイン失敗を通知する mu-plugin を作る

コードはテーマの functions.php ではなく、mu-plugin(必須プラグイン)として置きます。テーマを切り替えても止まらず、管理画面のプラグイン一覧から誤って停止されることもありません。

wp-content/mu-plugins/ フォルダーを作り(無ければ)、その中に login-failure-alert.php を次の内容で作成します。mu-plugins 直下の PHP ファイルは、有効化の操作をしなくても自動で読み込まれます。

<?php
/**
 * Plugin Name: Login Failure Alert
 * Description: 同じIPアドレスからのログイン失敗が続いたら、管理者にメールで知らせる。
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'wp_login_failed', 'lfa_on_login_failed', 10, 2 );

function lfa_on_login_failed( $username, $error = null ) {
	$threshold = 5;        // この回数続けて失敗したら通知する
	$window    = 900;      // 失敗回数を覚えておく秒数(最後の失敗から15分)
	$cooldown  = 3600;     // 同じIPについて次に通知するまでの秒数(1時間)

	$ip  = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown';
	$key = 'lfa_' . md5( $ip );

	// 失敗回数を数える
	$count = (int) get_transient( $key . '_count' ) + 1;
	set_transient( $key . '_count', $count, $window );

	if ( $count < $threshold ) {
		return;
	}

	// 通知済みなら送らない(メールの大量送信を防ぐ)
	if ( get_transient( $key . '_sent' ) ) {
		return;
	}
	set_transient( $key . '_sent', 1, $cooldown );

	$site    = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
	$subject = sprintf( '[%s] ログインの失敗が続いています', $site );
	$message = sprintf(
		"同じIPアドレスからログインの失敗が %d 回続きました。\n\n"
		. "IPアドレス: %s\n"
		. "最後に入力されたユーザー名: %s\n"
		. "エラーの種類: %s\n"
		. "日時: %s\n"
		. "サイト: %s\n",
		$count,
		$ip,
		sanitize_text_field( $username ),
		is_wp_error( $error ) ? $error->get_error_code() : '-',
		wp_date( 'Y-m-d H:i:s' ),
		home_url( '/' )
	);

	wp_mail( get_option( 'admin_email' ), $subject, $message );
}

コードの要点は次のとおりです。

  • 送信先は「設定」→「一般」の「管理者メールアドレス」(admin_email)です。
  • set_transient() は失敗のたびに期限を延ばすため、「最後の失敗から 15 分以内に次の失敗があれば数え続ける」動きになります。
  • しきい値・期間・再送間隔は先頭の 3 つの変数で変えられます。
  • 失敗回数を数えるだけで、ログインを止める(ロックする)機能はありません。止める方法は後の節で紹介します。

手順2: 管理者のログインも通知する(任意)

失敗だけでなく、管理者アカウントでログインに成功したときに本人へ通知すると、パスワードが漏れて不正にログインされた場合に気づけます。ログイン成功時は wp_login アクション(引数はユーザー名と WP_User)が実行されます。

wp-content/mu-plugins/admin-login-notice.php を作成します。

<?php
/**
 * Plugin Name: Admin Login Notice
 * Description: 管理者がログインしたら、そのユーザーにメールで知らせる。
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'wp_login', 'aln_on_login', 10, 2 );

function aln_on_login( $user_login, $user ) {
	// 管理者権限を持つユーザーだけ通知する
	if ( ! user_can( $user, 'manage_options' ) ) {
		return;
	}

	$ip      = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown';
	$site    = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
	$subject = sprintf( '[%s] 管理者アカウントでログインがありました', $site );
	$message = sprintf(
		"ユーザー名: %s\nIPアドレス: %s\n日時: %s\n\n心当たりがない場合は、すぐにパスワードを変更してください。\n",
		$user_login,
		$ip,
		wp_date( 'Y-m-d H:i:s' )
	);

	wp_mail( $user->user_email, $subject, $message );
}

自分がログインするたびにメールが届くことになります。管理者が頻繁にログインするサイトでは、届く量を見て使うかどうかを決めてください。

手順3: 動作を確かめる

  1. ログアウトした状態(別のブラウザーやプライベートウィンドウ)でログイン画面を開く
  2. 存在するユーザー名と誤ったパスワードで 5 回ログインを試す
  3. 管理者メールアドレスに「ログインの失敗が続いています」という件名のメールが届くことを確認する
  4. 続けて失敗しても、1 時間は同じ IP アドレスについて再送されないことを確認する

検証環境で実行したところ、5 回目の失敗で 1 通だけ送られ、6 回目以降は送られませんでした。届いたメールは次のような内容です。

件名: [サイト名] ログインの失敗が続いています

同じIPアドレスからログインの失敗が 5 回続きました。

IPアドレス: 203.0.113.9
最後に入力されたユーザー名: admin
エラーの種類: incorrect_password
日時: 2026-10-29 17:23:07
サイト: https://example.com/

メールが届かない場合は、WordPress 自体がメールを送れていない可能性があります。「WordPressのメールが送信されない問題を解決する方法」を参照し、SMTP 経由で送るように設定してください。

注意点

プロキシや CDN の後ろでは IP アドレスが正しく取れない

Cloudflare などの CDN やリバースプロキシを経由している場合、$_SERVER['REMOTE_ADDR'] にはプロキシの IP アドレスが入ります。この状態ではすべての訪問者が同じ IP アドレスとして数えられます。

X-Forwarded-For などのヘッダーは利用者が自由に書き換えられるため、PHP の中でそのまま信用するのは避けます。Web サーバー側で、信頼できるプロキシからのヘッダーだけを使って本来の IP アドレスを REMOTE_ADDR に入れる設定(Nginx の real_ip モジュール、Apache の mod_remoteip)を行ってください。

オブジェクトキャッシュを使っている場合

transient は通常データベースに保存されますが、Redis などの永続オブジェクトキャッシュを使っている場合はそちらに保存されます。どちらでも動作は同じですが、キャッシュを消去すると数えていた失敗回数と通知済みの記録も消えます。

通知だけでは攻撃は止まらない

このコードは「気づく」ための仕組みです。総当たり攻撃を止めるには、次のような対策と組み合わせます。

メールに含まれる情報の扱い

通知メールには IP アドレスや入力されたユーザー名が含まれます。入力されたユーザー名が実在するかどうか(incorrect_password か invalid_username か)も分かるため、転送先や共有範囲に注意してください。

まとめ

  • WordPress はログイン失敗時に wp_login_failed を実行し、入力されたユーザー名と失敗の理由(WP_Error)を渡します。
  • IP アドレスごとに transient で失敗回数を数え、しきい値に達したら 1 回だけ wp_mail() で通知すると、メールが大量に届きません。
  • コードは mu-plugin に置くと、テーマの切り替えやプラグインの停止の影響を受けません。
  • プロキシの後ろでは Web サーバー側で本来の IP アドレスを渡す設定が必要です。
  • 通知は気づくための仕組みなので、試行回数の制限やアクセス制限と組み合わせます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲