最近我的网站后台频繁遭到爆破,日志里充斥着各种弱密码尝试。
为降低安全风险,避免恶意请求反复占用服务器资源,我决定在 WordPress 登录页加入一个简单的验证码。
这样能在用户输入密码的同时,多一道校验,拦截绝大多数自动化爆破工具。
首先进入后台,在侧边栏——外观——主题文件编辑器中,选择functions.php,在文件内容底部添加代码:
function start_session_for_captcha() {
if ( in_array($GLOBALS['pagenow'], ['wp-login.php','wp-register.php']) && !session_id() ) {
@ob_start();
session_start();
// 不要立即关闭 session,让登录验证能读取
}
}
add_action('init', 'start_session_for_captcha', 1);
// 生成验证码表达式和答案
function generate_captcha() {
$num1 = rand(10, 99);
$num2 = rand(10, 99);
$operator = ['+', '-'][rand(0,1)];
if ($operator === '-' && $num1 < $num2) {
list($num1, $num2) = [$num2, $num1];
}
$_SESSION['captcha_answer'] = ($operator === '+') ? $num1 + $num2 : $num1 - $num2;
$_SESSION['captcha_expression'] = "$num1 $operator $num2";
return $_SESSION['captcha_expression'];
}
// 登录表单显示验证码
function add_captcha_to_login_form() {
if (!isset($_SESSION['captcha_expression'])) {
generate_captcha();
}
$expression = $_SESSION['captcha_expression'];
echo "
<p id='login-captcha' style='display:flex; align-items:center; gap:8px;'>
<label for='captcha' style='margin:0;'>验证码:$expression = ?</label>
<input type='text' name='captcha' id='captcha' autocomplete='off' required style='width:80px;'>
<span id='refresh-captcha' title='刷新验证码' style='cursor:pointer;'>↻</span>
</p>
<script>
document.getElementById('refresh-captcha').addEventListener('click', function() {
const btn = this;
btn.style.transition='transform 0.3s';
btn.style.transform='rotate(360deg)';
setTimeout(()=>{btn.style.transform='rotate(0deg)';},300);
fetch('" . admin_url('admin-ajax.php') . "?action=refresh_captcha&_=' + new Date().getTime())
.then(r=>r.json())
.then(data=>{
if(data.success){
document.querySelector('#login-captcha label').innerText='验证码:'+data.data.expression+' = ?';
document.querySelector('#captcha').value='';
}else{
alert(data.data || '刷新失败,请重试');
}
}).catch(e=>{
console.error('刷新验证码失败:',e);
alert('刷新验证码失败,请重试');
});
});
</script>";
}
add_action('login_form', 'add_captcha_to_login_form');
// Ajax 刷新验证码
function ajax_refresh_captcha() {
if (!session_id()) session_start();
$expression = generate_captcha();
session_write_close(); // 仅在 Ajax 刷新后释放锁
wp_send_json_success(['expression'=>$expression]);
}
add_action('wp_ajax_refresh_captcha', 'ajax_refresh_captcha');
add_action('wp_ajax_nopriv_refresh_captcha', 'ajax_refresh_captcha');
// 登录验证
function validate_captcha_on_login($user, $username, $password){
if(!session_id()) session_start();
if(!isset($_POST['captcha']) || !is_numeric($_POST['captcha'])){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码必须为数字.');
}
$input = intval($_POST['captcha']);
if(!isset($_SESSION['captcha_answer']) || $input !== $_SESSION['captcha_answer']){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码错误,请重试.');
}
// 验证通过,清理 session
unset($_SESSION['captcha_answer'], $_SESSION['captcha_expression']);
return $user;
}
add_filter('authenticate','validate_captcha_on_login',30,3);👆🏻🤓建议:保存后先在浏览器无痕模式下测试,确认验证码功能正常后再投入使用。
值得注意的是validate_captcha_on_login部分:
function validate_captcha_on_login($user, $username, $password){
if(!session_id()) session_start();
if(!isset($_POST['captcha']) || !is_numeric($_POST['captcha'])){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码必须为数字.');
}
$input = intval($_POST['captcha']);
if(!isset($_SESSION['captcha_answer']) || $input !== $_SESSION['captcha_answer']){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码错误,请重试.');
}
// 验证通过,清理 session
unset($_SESSION['captcha_answer'], $_SESSION['captcha_expression']);
return $user;
}中的关键位置:
if(!isset($_SESSION['captcha_answer'])
|| $input !==
$_SESSION['captcha_answer'])这里不区分用户名是否存在,直接判断验证码是否正确。
也就是说:
不管用户名存在与否,只要验证码不对(或者会话里没有答案),就统一返回 “验证码错误”。
即使用户名不存在也返回验证码错误,以达到迷惑攻击者的目的。
各位站长在部署时稍加注意,别被自己的门闩挡在外面了
旧代码,因干扰了 REST API 及环回请求被弃置(拿来做警示展出,勿用):
// 启动 Session
function start_session_for_captcha() {
if (!session_id()) {
@ob_start();
session_start();
}
}
add_action('init', 'start_session_for_captcha', 1);
// 生成验证码
function generate_captcha() {
$num1 = rand(10, 99);
$num2 = rand(10, 99);
$operator = ['+', '-'][rand(0,1)];
if ($operator === '-' && $num1 < $num2) {
list($num1, $num2) = [$num2, $num1];
}
$_SESSION['captcha_answer'] = ($operator === '+') ? $num1 + $num2 : $num1 - $num2;
return "$num1 $operator $num2";
}
// 登录表单显示验证码
function add_captcha_to_login_form() {
// 只有当 Session 没有答案时生成一次
if (!isset($_SESSION['captcha_answer'])) {
$expression = generate_captcha();
} else {
// 如果已有答案,显示当前的公式
$answer = $_SESSION['captcha_answer'];
$expression = ''; // 为了安全,这里可以保留空,或者生成表达式存储在 $_SESSION['captcha_expression']
}
echo "
<p id='login-captcha' style='display:flex; align-items:center; gap:8px;'>
<label for='captcha' style='margin:0;'>验证码:" . (isset($_SESSION['captcha_expression']) ? $_SESSION['captcha_expression'] : generate_captcha()) . " = ?</label>
<input type='text' name='captcha' id='captcha' autocomplete='off' required style='width:80px;'>
<span id='refresh-captcha' title='刷新验证码' style='cursor:pointer;'>↻</span>
</p>
<script>
document.getElementById('refresh-captcha').addEventListener('click', function() {
const btn = this;
btn.style.transition='transform 0.3s';
btn.style.transform='rotate(360deg)';
setTimeout(()=>{btn.style.transform='rotate(0deg)';},300);
fetch('" . admin_url('admin-ajax.php') . "?action=refresh_captcha&_=' + new Date().getTime())
.then(r=>r.json())
.then(data=>{
if(data.success){
document.querySelector('#login-captcha label').innerText='验证码:'+data.data.expression+' = ?';
document.querySelector('#captcha').value='';
}else{
alert(data.data || '刷新失败,请重试');
}
}).catch(e=>{
console.error('刷新验证码失败:',e);
alert('刷新验证码失败,请重试');
});
});
</script>";
}
add_action('login_form', 'add_captcha_to_login_form');
// AJAX 刷新验证码
function ajax_refresh_captcha() {
if (!session_id()) session_start();
$expression = generate_captcha();
$_SESSION['captcha_expression'] = $expression; // 存储公式
wp_send_json_success(['expression'=>$expression]);
}
add_action('wp_ajax_refresh_captcha', 'ajax_refresh_captcha');
add_action('wp_ajax_nopriv_refresh_captcha', 'ajax_refresh_captcha');
// 登录验证
function validate_captcha_on_login($user,$username,$password){
if(!isset($_POST['captcha']) || !is_numeric($_POST['captcha'])){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码必须为数字.');
}
$input=intval($_POST['captcha']);
if(!isset($_SESSION['captcha_answer']) || $input!==$_SESSION['captcha_answer']){
return new WP_Error('captcha_error','<strong>错误</strong>: 验证码错误,请重试.');
}
unset($_SESSION['captcha_answer'], $_SESSION['captcha_expression']);
return $user;
}
add_filter('authenticate','validate_captcha_on_login',30,3);转载请标明出处
由ChatGPT协助完成
评论