WordPressでプラグインを使わずに管理画面の不正アクセス対策を行う
WordPressのプラグインは便利なのですが、最近はちょっと“邪悪”なものが増えてきた気がします。アップデートで別物になったり、急に課金前提になったり…。
というわけで、プラグインを削除してアクセス制限の処理を自作してみました。
管理画面の不正ログインを制限
「functions.php」に追記して実行します。
<?php
///=======================================================================
// *管理画面の不正ログインを制限
///=======================================================================
define('IB_MAX_LOGIN_ATTEMPTS', 3); // 何回連続で失敗したらロックするか
define('IB_LOCKOUT_MINUTES', 60); // ロック時間(分単位)
//---------------------------------------------
// *クライアントIPを取得
//---------------------------------------------
function ib_get_client_ip(){
foreach(['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'] as $key){
if(!empty($_SERVER[$key])){
//---カンマ区切りの場合は先頭だけ使う
$ip = explode(',', $_SERVER[$key])[0];
return trim($ip);
}
}
return 'unknown';
}
//---------------------------------------------
// *ログイン失敗時にカウントアップ
//---------------------------------------------
function ib_wp_login_failed($username){
$ip = ib_get_client_ip();
$state = ib_get_login_state($ip);
if($state['locked_until'] > time()){
return;
}
$state['count']++;
$state['last_username'] = $username;
if($state['count'] >= IB_MAX_LOGIN_ATTEMPTS){
$state['locked_until'] = time() + (IB_LOCKOUT_MINUTES * 60);
}
ib_set_login_state($ip, $state);
}
add_action("wp_login_failed", "ib_wp_login_failed");
//---------------------------------------------
// *IPごとの失敗状態を取得
//---------------------------------------------
function ib_get_login_state($ip){
$key = 'ib_login_limit_' . md5($ip);
$state = get_transient($key);
if(!is_array($state)){
$state = [
'ip' => $ip,
'count' => 0,
'locked_until' => 0,
'updated_at' => 0,
'last_username' => '',
'logged_in' => 0,
];
}
return $state;
}
//---------------------------------------------
// *IPごとの失敗状態を保存
//---------------------------------------------
function ib_set_login_state($ip, $state){
$key = 'ib_login_limit_' . md5($ip);
$state['ip'] = $ip;
$state['updated_at'] = time();
//---ロック時間 + 余裕分だけ保持(ここでは24時間)
$expiration = (IB_LOCKOUT_MINUTES * 60) + 86400;
set_transient($key, $state, $expiration);
}
//---------------------------------------------
// *認証成功時にカウンタをリセット
//---------------------------------------------
function ib_authenticate($user, $username, $password){
$ip = ib_get_client_ip();
$state = ib_get_login_state($ip);
//---ログインに成功している
if($user instanceof WP_User){
$state['count'] = 0;
$state['locked_until'] = 0;
$state['last_username'] = $username;
$state['logged_in'] = 1;
ib_set_login_state($ip, $state);
}
return $user;
}
add_filter("authenticate", "ib_authenticate", 30, 3);
//---------------------------------------------
// *ロック時にログインフォームで403
//---------------------------------------------
function ib_block_locked_ip_before_form(){
$ip = ib_get_client_ip();
$state = ib_get_login_state($ip);
if($state['locked_until'] <= time()){
return;
}
$remaining = $state['locked_until'] - time();
$min = ceil($remaining / 60);
if($min < 1){
$min = 1;
}
wp_die(
"このIPアドレス({$ip})からのログイン試行回数が多すぎます。<br>あと {$min} 分後に再度お試しください。",
'ログイン制限中',
[
'response' => 403,
'back_link' => false,
]
);
}
add_action('login_init', 'ib_block_locked_ip_before_form');
アクセス制限の内容
過去24時間以内に3回以上アクセスに失敗したIPアドレスに対しては、60分間「403 エラー」を返してアクセスを制限しています。

403 エラーを返すことで、お行儀の良いBOTはこちらへのアクセスを諦めてくれます。
実際、単にコメント欄に「エラーです」と表示してもBOTのアクセスは止まりませんでしたが、403 エラーを返すようにしたところアクセスが止まりました(攻撃側にとっても無駄な時間だと判断されるためです)。
意外と簡単だったので、今後もプラグインを自作して置き換えていきたいと思います!

