为什么需要后台登录验证码?
WordPress后台登录界面是黑客攻击的常见目标。暴力破解、撞库攻击、爬虫尝试等安全威胁时刻存在。添加算术验证码功能可以有效:
- 阻挡自动化攻击:防止机器人暴力尝试登录
- 提升安全性:增加额外的验证层
- 简单有效:用户体验友好,易于实现
- 零成本防护:无需第三方服务,自托管解决方案
算术验证码的实现原理
算术验证码通过简单的数学计算问题(如”3 + 5 = ?”)来验证操作者是否为人类。相对于传统图形验证码,算术验证码具有以下优势:
- 无障碍友好:不依赖视觉识别
- 加载快速:无需生成和传输图片
- 易于实现:几行代码即可完成
- 用户体验佳:输入简单,计算容易
完整实现方案
方案一:通过主题functions.php文件实现(推荐)
将以下代码添加到当前主题的functions.php文件末尾:
<?php
/**
* WordPress后台登录添加算术验证码功能
* 添加时间:2024年1月
*/
// 1. 在登录表单添加算术验证码字段
add_action('login_form', 'custom_login_arithmetic_captcha');
function custom_login_arithmetic_captcha() {
// 生成两个随机数
$num1 = rand(1, 10);
$num2 = rand(1, 10);
// 存储正确答案到临时选项,有效期为2小时
$correct_answer = $num1 + $num2;
set_transient('login_captcha_answer', $correct_answer, 2 * HOUR_IN_SECONDS);
// 存储算式到临时选项
$equation = "$num1 + $num2";
set_transient('login_captcha_equation', $equation, 2 * HOUR_IN_SECONDS);
// 输出验证码输入框
?>
<p>
<label for="arithmetic_captcha">安全验证<br/>
<strong><?php echo $equation; ?> = ?</strong><br/>
<input type="text" name="arithmetic_captcha" id="arithmetic_captcha" class="input" value="" size="20" autocomplete="off" />
</label>
</p>
<input type="hidden" name="captcha_equation" value="<?php echo $equation; ?>" />
<?php
}
// 2. 验证算术验证码
add_action('wp_authenticate_user', 'validate_arithmetic_captcha', 10, 2);
function validate_arithmetic_captcha($user, $password) {
// 如果已经通过验证或是XML-RPC请求,跳过验证
if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
return $user;
}
// 检查是否正在登录
if (!isset($_POST['wp-submit'])) {
return $user;
}
// 获取存储的正确答案
$stored_answer = get_transient('login_captcha_answer');
// 清理临时数据
delete_transient('login_captcha_answer');
delete_transient('login_captcha_equation');
// 验证用户输入
if (!isset($_POST['arithmetic_captcha']) || empty($_POST['arithmetic_captcha'])) {
return new WP_Error('captcha_empty', '<strong>错误</strong>: 请输入算术验证码结果。');
}
$user_answer = intval($_POST['arithmetic_captcha']);
if ($stored_answer === false) {
return new WP_Error('captcha_expired', '<strong>错误</strong>: 验证码已过期,请刷新页面重试。');
}
if ($user_answer != $stored_answer) {
return new WP_Error('captcha_wrong', '<strong>错误</strong>: 算术验证码答案错误。');
}
return $user;
}
// 3. 防止登录页面缓存,确保每次验证码不同
add_action('login_head', 'disable_login_page_caching');
function disable_login_page_caching() {
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
}
// 4. 添加验证码样式
add_action('login_head', 'custom_captcha_styles');
function custom_captcha_styles() {
?>
<style type="text/css">
#arithmetic_captcha {
width: 100%;
padding: 3px 8px;
font-size: 24px;
line-height: 1.5;
height: 50px;
margin-top: 5px;
margin-bottom: 15px;
}
label[for="arithmetic_captcha"] strong {
font-size: 20px;
color: #23282d;
display: block;
margin-bottom: 10px;
padding: 8px 12px;
background: #f7f7f7;
border: 1px solid #ddd;
border-radius: 4px;
}
.login form p:has(#arithmetic_captcha) {
margin-bottom: 20px;
}
</style>
<?php
}
// 5. 可选:为特定用户角色跳过验证码(例如管理员已启用双重验证)
add_filter('allow_password_reset', 'maybe_skip_captcha_for_trusted_users', 10, 2);
function maybe_skip_captcha_for_trusted_users($allowed, $user_id) {
// 如果已启用双重验证或其他安全措施,可在此添加跳过逻辑
return $allowed;
}
?>
方案二:创建独立插件实现
创建文件:wp-content/plugins/arithmetic-login-captcha/arithmetic-login-captcha.php
<?php
/**
* Plugin Name: Arithmetic Login Captcha
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress后台登录添加算术验证码功能
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class Arithmetic_Login_Captcha {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action('login_form', array($this, 'add_captcha_field'));
add_action('wp_authenticate_user', array($this, 'validate_captcha'), 10, 2);
add_action('login_head', array($this, 'add_captcha_styles'));
add_action('login_head', array($this, 'disable_caching'));
add_action('login_enqueue_scripts', array($this, 'enqueue_scripts'));
// 可选的:添加设置页面
add_action('admin_menu', array($this, 'add_settings_page'));
add_action('admin_init', array($this, 'register_settings'));
}
public function add_captcha_field() {
$num1 = rand(1, 15);
$num2 = rand(1, 15);
// 可选:随机选择运算符
$operators = array('+', '-', '×');
$operator = $operators[array_rand($operators)];
switch ($operator) {
case '+':
$answer = $num1 + $num2;
break;
case '-':
// 确保结果为正数
if ($num1 < $num2) {
$temp = $num1;
$num1 = $num2;
$num2 = $temp;
}
$answer = $num1 - $num2;
break;
case '×':
$num1 = rand(1, 9);
$num2 = rand(1, 9);
$answer = $num1 * $num2;
break;
}
// 存储答案,使用用户IP作为部分密钥防止重复利用
$user_ip = $this->get_user_ip();
$captcha_key = 'alc_' . md5($user_ip . '|' . $answer);
set_transient($captcha_key, $answer, 300); // 5分钟有效
?>
<p>
<label for="arithmetic_captcha">安全验证<br/>
<strong><?php echo $num1 . ' ' . $operator . ' ' . $num2 . ' = ?'; ?></strong><br/>
<input type="text" name="arithmetic_captcha" id="arithmetic_captcha"
class="input" value="" size="20" autocomplete="off"
placeholder="请输入计算结果" />
</label>
</p>
<input type="hidden" name="captcha_key" value="<?php echo esc_attr($captcha_key); ?>" />
<?php
}
public function validate_captcha($user, $password) {
// 跳过REST API、XML-RPC和AJAX请求
if (defined('REST_REQUEST') && REST_REQUEST ||
defined('XMLRPC_REQUEST') && XMLRPC_REQUEST ||
defined('DOING_AJAX') && DOING_AJAX) {
return $user;
}
if (!isset($_POST['wp-submit'])) {
return $user;
}
if (!isset($_POST['captcha_key']) || !isset($_POST['arithmetic_captcha'])) {
return new WP_Error('captcha_missing',
'<strong>错误</strong>: 验证码信息不完整。');
}
$captcha_key = sanitize_text_field($_POST['captcha_key']);
$user_answer = intval($_POST['arithmetic_captcha']);
$correct_answer = get_transient($captcha_key);
// 清理使用过的验证码
delete_transient($captcha_key);
if ($correct_answer === false) {
return new WP_Error('captcha_expired',
'<strong>错误</strong>: 验证码已过期,请刷新页面重试。');
}
if ($user_answer != $correct_answer) {
// 可选的:记录失败尝试
$this->log_failed_attempt();
return new WP_Error('captcha_incorrect',
'<strong>错误</strong>: 验证码答案错误,请重试。');
}
return $user;
}
public function add_captcha_styles() {
?>
<style type="text/css">
#loginform p:has(#arithmetic_captcha) {
margin-bottom: 20px;
}
#arithmetic_captcha {
width: 100%;
height: 46px;
font-size: 20px;
text-align: center;
font-weight: bold;
margin-top: 8px;
margin-bottom: 5px;
}
label[for="arithmetic_captcha"] strong {
display: block;
font-size: 22px;
color: #1d2327;
background: #f0f6fc;
border: 1px solid #8c8f94;
padding: 12px;
border-radius: 4px;
text-align: center;
margin-bottom: 8px;
}
.captcha-hint {
font-size: 12px;
color: #646970;
margin-top: 5px;
display: block;
}
</style>
<?php
}
public function disable_caching() {
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
}
public function enqueue_scripts() {
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
var captchaInput = document.getElementById('arithmetic_captcha');
if (captchaInput) {
captchaInput.focus();
// 自动切换运算符显示
var equationElement = document.querySelector('label[for="arithmetic_captcha"] strong');
if (equationElement) {
var equation = equationElement.textContent;
equation = equation.replace('×', '*');
equationElement.textContent = equation;
}
}
});
</script>
<?php
}
public function add_settings_page() {
add_options_page(
'算术验证码设置',
'登录验证码',
'manage_options',
'arithmetic-captcha',
array($this, 'render_settings_page')
);
}
public function register_settings() {
register_setting('arithmetic_captcha_settings', 'alc_difficulty');
register_setting('arithmetic_captcha_settings', 'alc_enable_for_users');
add_settings_section(
'alc_main_section',
'验证码设置',
null,
'arithmetic-captcha'
);
add_settings_field(
'alc_difficulty',
'计算难度',
array($this, 'difficulty_field_callback'),
'arithmetic-captcha',
'alc_main_section'
);
}
public function difficulty_field_callback() {
$difficulty = get_option('alc_difficulty', 'easy');
?>
<select name="alc_difficulty">
<option value="easy" <?php selected($difficulty, 'easy'); ?>>简单 (1-10)</option>
<option value="medium" <?php selected($difficulty, 'medium'); ?>>中等 (1-20)</option>
<option value="hard" <?php selected($difficulty, 'hard'); ?>>困难 (1-50)</option>
</select>
<p class="description">选择算术验证码的计算难度。</p>
<?php
}
public function render_settings_page() {
?>
<div class="wrap">
<h1>算术验证码设置</h1>
<form action="options.php" method="post">
<?php
settings_fields('arithmetic_captcha_settings');
do_settings_sections('arithmetic-captcha');
submit_button();
?>
</form>
</div>
<?php
}
private function get_user_ip() {
$ip = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
private function log_failed_attempt() {
$log_file = WP_CONTENT_DIR . '/captcha_failed_log.txt';
$entry = sprintf("[%s] IP: %s, User Agent: %s\n",
current_time('mysql'),
$this->get_user_ip(),
$_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'
);
@file_put_contents($log_file, $entry, FILE_APPEND);
}
}
// 初始化插件
Arithmetic_Login_Captcha::get_instance();
?>
高级功能扩展
添加AJAX刷新验证码功能
在插件版本基础上,添加以下代码实现无刷新更换验证码:
// 在类中添加AJAX处理
add_action('wp_ajax_nopriv_refresh_captcha', array($this, 'ajax_refresh_captcha'));
add_action('wp_ajax_refresh_captcha', array($this, 'ajax_refresh_captcha'));
public function ajax_refresh_captcha() {
$num1 = rand(1, 10);
$num2 = rand(1, 10);
$answer = $num1 + $num2;
$user_ip = $this->get_user_ip();
$captcha_key = 'alc_' . md5($user_ip . '|' . $answer);
set_transient($captcha_key, $answer, 300);
wp_send_json_success(array(
'equation' => "$num1 + $num2",
'key' => $captcha_key
));
}
// 添加刷新按钮的JavaScript
public function enqueue_scripts() {
?>
<style type="text/css">
.captcha-refresh {
margin-left: 10px;
cursor: pointer;
color: #2271b1;
text-decoration: underline;
font-size: 13px;
}
</style>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('#refresh-captcha').on('click', function(e) {
e.preventDefault();
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
data: {
action: 'refresh_captcha'
},
success: function(response) {
if (response.success) {
$('label[for="arithmetic_captcha"] strong').text(response.data.equation + ' = ?');
$('input[name="captcha_key"]').val(response.data.key);
$('#arithmetic_captcha').val('').focus();
}
}
});
});
});
</script>
<?php
}
// 在验证码字段后添加刷新按钮
// 修改add_captcha_field函数,在验证码字段后添加:
<span class="captcha-refresh" id="refresh-captcha">刷新验证码</span>
安装和配置指南
方法一:通过主题functions.php安装
- 登录WordPress后台
- 进入 外观 → 主题编辑器
- 选择当前主题的functions.php文件
- 将方案一的代码添加到文件末尾
- 点击“更新文件”
方法二:通过插件安装
- 将方案二的插件代码保存为
arithmetic-login-captcha.php - 通过FTP上传到
/wp-content/plugins/目录 - 或在后台 插件 → 安装插件 → 上传插件
- 激活插件
方法三:通过代码片段插件安装
- 安装并激活”Code Snippets”插件
- 添加新代码片段
- 粘贴方案一的代码
- 设置仅在后台生效
- 保存并激活
测试和验证
测试步骤:
- 注销当前登录状态
- 访问
/wp-login.php - 查看是否显示算术验证码
- 故意输入错误答案,查看错误提示
- 输入正确答案,验证登录功能
- 测试XML-RPC功能(如适用)
常见问题排查:
问题1:验证码不显示
- 检查代码是否正确添加到functions.php
- 清除浏览器缓存
- 检查是否有其他插件冲突
问题2:始终验证失败
- 检查服务器时区设置
- 确认transient功能正常工作
- 检查是否有缓存插件干扰
问题3:影响其他功能
- 验证已排除XML-RPC和REST API
- 检查登录相关钩子优先级
- 测试密码重置功能
安全增强建议
1. 增加失败尝试限制
// 在验证失败时增加计数
$attempts = get_transient('login_attempts_' . $user_ip) ?: 0;
$attempts++;
set_transient('login_attempts_' . $user_ip, $attempts, 15 * MINUTE_IN_SECONDS);
if ($attempts > 5) {
return new WP_Error('too_many_attempts',
'<strong>错误</strong>: 尝试次数过多,请15分钟后再试。');
}
2. 添加蜜罐字段
// 在登录表单添加隐藏字段
add_action('login_form', 'add_honeypot_field');
function add_honeypot_field() {
echo '<input type="text" name="website" id="website" style="display:none !important; visibility:hidden !important;" tabindex="-1" autocomplete="off" />';
}
// 验证时检查
if (!empty($_POST['website'])) {
// 可能是机器人,直接失败
wp_die('检测到可疑活动。');
}
3. 记录登录尝试
// 记录所有登录尝试
function log_login_attempt($username, $status) {
$log_entry = sprintf(
"[%s] 用户名: %s, IP: %s, 状态: %s\n",
current_time('mysql'),
$username,
$_SERVER['REMOTE_ADDR'],
$status
);
file_put_contents(WP_CONTENT_DIR . '/login_attempts.log', $log_entry, FILE_APPEND);
}
性能优化
1. 使用对象缓存替代Transient
如果安装了Redis或Memcached:
wp_cache_set($cache_key, $answer, 'login_captcha', 300);
$answer = wp_cache_get($cache_key, 'login_captcha');
2. 精简验证逻辑
避免复杂的计算,保持验证过程轻量级。
注意事项
- 备份重要文件:修改functions.php前务必备份
- 子主题优先:如果使用子主题,修改子主题的functions.php
- 兼容性测试:确保与其他安全插件兼容
- 用户通知:如果网站有多个用户,提前通知添加了验证码
- 备用方案:保留管理员重置验证码的方法
总结
添加算术验证码是提升WordPress后台安全性的有效且简单的方法。本文提供了两种实现方案:
- 简易方案:适合快速部署,通过主题functions.php实现
- 插件方案:功能更完整,易于管理和扩展
无论选择哪种方案,都能显著提升网站安全性,阻挡大多数自动化攻击。建议配合其他安全措施,如限制登录尝试、使用强密码、启用双重验证等,构建多层次的安全防护体系。
最后提醒:安全是一个持续的过程,定期更新代码、监控登录日志、及时响应异常活动,才能确保网站的长期安全稳定运行。


湘公网安备43020002000238