cc防御

“纸上得来终觉浅,绝知此事要躬行。” —— 陆游《冬夜读书示子聿》

在互联网的世界里,网站就像一座城堡,而登录界面则是它的正门。如果这扇门没有锁好,黑客就像“潜入者”,随时可能破门而入。

为了防止机器人、暴力破解等恶意行为,我们可以在登录页加入一个数学题验证码功能。今天,就让我们一起动手实现这个小功能——即使你是编程新手,也能一步步完成!


🧩 一、我们的目标:打造一个会出题的登录门卫

我们要做的,是在 WordPress 默认登录页面 上添加一个简单的验证码功能:

  • 显示一道数学题
  • 用户必须输入正确答案才能登录
  • 错了?不准进!提示错误信息
  • 验证码过期?可以刷新重来

听起来是不是很酷?别担心,接下来一步一步带你实现它。


🛠️ 二、准备工作:你不需要是程序员,但需要知道这些事

✅ 1. WordPress 插件机制简介

WordPress 是一个“可扩展”的系统,就像乐高积木,你可以通过插件给它加新功能。

我们不会改动 WordPress 的核心文件,而是创建一个自己的插件,这样既安全又容易维护。

✅ 2. PHP 基础知识(不用太深奥)

本教程使用的是 PHP,因为 WordPress 就是用 PHP 写的。你不需要成为高手,只要了解以下几点就够了:

  • session_start():开启会话,保存验证码结果
  • imagepng():动态生成图片
  • add_action()add_filter():这是 WordPress 的钩子系统,可以理解为“插入自定义代码的位置”

✅ 3. 开发工具推荐

  • 代码编辑器:VS Code
  • FTP 工具:FileZilla
  • 浏览器调试工具:Chrome 开发者工具(F12)
  • 1Panel/宝塔运维面板

有了这些工具,你就可以开始搭建你的“数字堡垒”了。


🔁 三、验证码的工作原理:像门卫查身份证一样严谨

验证码的核心逻辑其实很简单,就像门卫查身份证一样:

  1. 系统生成一个数学题,比如:“5 + 3 = ?”
  2. 把正确答案存在服务器上(Session)
  3. 用户输入答案后,系统比对是否一致
  4. 如果不一致或过期,就拦住不让进

整个过程就像是设置了一道“身份验证关卡”,确保来的不是机器人,而是真实用户。


📦 四、动手实战:手把手教你写出属于你的验证码插件

🔧 第一步:创建插件目录和主文件

进入你的 WordPress 安装目录:

Bash
/wp-content/plugins/

新建一个文件夹,例如:captcha-login

在这个文件夹中,新建一个文件:captcha-login.php

打开这个文件,写入如下内容作为插件头部:

PHP
<?php
/*
Plugin Name: Captcha for Login
Description: 在 WordPress 登录页添加数学验证码功能
Version: 1.0
Author: Your Name
*/

if (!defined('ABSPATH')) {
    exit; // 禁止直接访问
}

// 加载核心逻辑文件
include plugin_dir_path(__FILE__) . 'includes/functions.php';

恭喜你,已经完成了第一步!


🧮 第二步:创建核心文件

在插件目录下新建一个文件夹:

Bash
/captcha-login/includes/

然后创建 functions.php 文件,写入以下代码:

PHP
<?php
if (!defined('ABSPATH')) {
    exit;
}

// 启动Session
add_action('init','start_session');
function start_session() {
    if (!session_id()) {
        @session_start();
    }
}


// 添加验证码字段到登录表单
add_action('login_form', 'display_captcha_on_login');
function display_captcha_on_login() {
  
    echo '<p>
            <label for="operation_captcha">验证码结果为:</label><br />
            <input type="text" name="operation_captcha" id="operation_captcha" size="20" tabindex="30" />
            <a href="#" id="refresh-captcha"><img src="' . esc_url(plugins_url('refresh.png', __FILE__)) . '" alt="刷新验证码" /></a>
            <img src="' . esc_url(home_url('/captcha.png?t='. time())) . '" alt="验证码图片" style="vertical-align:middle;" />
          </p>';
    echo '<script>
            document.getElementById("refresh-captcha").addEventListener("click", function(e) {
                e.preventDefault();
                var img = document.querySelector("img[alt=验证码图片]");
                img.src = "' . esc_url(home_url('/captcha.png?t='. time())) . '";
                
            });
          </script>';      
}

// 注册重写规则以隐藏真实路径
add_action('init','captcha_rewrite_rule');
function captcha_rewrite_rule() {
    add_rewrite_rule('^captcha\.png$', 'index.php?__captcha=1', 'top');
}

// 添加自定义查询变量
add_filter('query_vars','captcha_query_var');
function captcha_query_var($vars) {
    $vars[] = '__captcha';
    return $vars;
}

// 输出验证码图片
add_action('template_redirect','captcha_image');
function captcha_image() {
    if (get_query_var('__captcha')) {
        include_once plugin_dir_path(__FILE__) . '/captcha.php';
        exit;
    }
}

// 验证用户输入的验证码
add_filter('authenticate','validate_captcha_login', 10, 3);
function validate_captcha_login($user,$username,$password) {

     // 只有在提交登录表单时才验证验证码
    if (!isset($_POST['log']) || !isset($_POST['pwd'])) {
        return $user;
    }
       
    //+ 空字符串检查
    $name = $_POST['operation_captcha'] ?? '';
 
    if ($name === '') {
        
        wp_redirect(wp_login_url() . '?cptchaerror=null');
        exit;

    }

    //生成验证码运算结果检查
    if (!isset($_SESSION['mcaptcha_result'])) {      

        wp_redirect(wp_login_url() . '?cptchaerror=resultnull');
        exit;
    }
    //验证码有效期检查
    if (isset($_SESSION['captcha_expiry']) && time() > $_SESSION['captcha_expiry']) {

        wp_redirect(wp_login_url() . '?cptchaerror=expired');
        exit;

    }

    $submitted = intval($_POST['operation_captcha']);
    $correct   = isset($_SESSION['captcha_result']) ? intval($_SESSION['captcha_result']) : null;
    
    //验证码校验
    if ($submitted !== $correct) {

        wp_redirect(wp_login_url() . '?cptchaerror=wrong');
        exit;

    }

    // 验证码通过后,继续执行 WordPress 默认的登录验证
    return wp_authenticate_username_password(null, $username, $password);
}

// 在wp-login.php页面显示错误信息
add_action('login_message', 'display_captcha_error');
function display_captcha_error($message) {

    if (isset($_GET['cptchaerror'])) {
        if ($_GET['cptchaerror'] === 'null' || $_GET['cptchaerror'] === 'resultnull') {
            $message .= '<div class="message error"><p><strong>ERROR</strong>: 请输入验证码。</p></div>';
        } elseif ($_GET['cptchaerror'] === 'wrong') {
            $message .= '<div class="message error"><p><strong>ERROR</strong>: 验证码不正确,请重试。</p></div>';
        } elseif ($_GET['cptchaerror'] === 'expired') {
            $message .= '<div class="message error"><p><strong>ERROR</strong>: 验证码已过期,请刷新重试。</p></div>';
        }
    }

    return $message;
}

// 清理Session数据
add_action('wp_logout','clear_captcha_session');
add_action('wp_login','clear_captcha_session');
function clear_captcha_session() {
    if (isset($_SESSION['captcha_result'])) {
        unset($_SESSION['captcha_result']);
    }
    if (isset($_SESSION['captcha_expiry'])) {
        unset($_SESSION['mcaptcha_expiry']);
    }
    if (isset($_SESSION['captcha_question'])) {
        unset($_SESSION['captcha_question']);
    }
}

📌 该文件拥有以下功能:

  • 启动Session
  • 添加验证码功能到登录界面
  • 注册重写规则以隐藏真实路径
  • 添加自定义查询变量
  • 引用输出验证码图片
  • 验证用户输入的验证码
  • 在wp-login.php页面显示错误信息
  • 清理Session数据。

🖼️ 第三步:生成验证码图片

/captcha-login/includes/路径下创建 captcha.php 文件,写入以下代码:

PHP
<?php

header("Content-type: image/png");

//写一个函数,让它自动出题
function math_Operation_captcha() {
    $operators = ['+', '-', '×', '÷'];
    $op = $operators[array_rand($operators)];

    $num1 = rand(1, 10);
    $num2 = rand(1, 10);

    switch ($op) {
        case '+':
            $result = $num1 + $num2;
            break;
        case '-':
            $result = $num1 - $num2;
            break;
        case '×':
            $result = $num1 * $num2;
            break;
        case '÷':
            while ($num2 == 0 || $num1 % $num2 != 0) {
                $num1 = rand(1, 10);
                $num2 = rand(1, 10);
            }
            $result = $num1 / $num2;
            break;
    }

    //返回一个类似 "5 × 7" 的题目,并把正确答案保存在 $_SESSION 中,供后续验证使用。
    $_SESSION['captcha_result'] = $result;
    $_SESSION['captcha_question'] = "$num1 $op $num2";
    $_SESSION['captcha_expiry'] = time() + 60; // 验证码有效期 60 秒
    return "$num1 $op $num2";
}

$im = imagecreatetruecolor(120, 40);

$bg_color = imagecolorallocate($im, 255, 255, 255); // 白色背景
$text_color = imagecolorallocate($im, 0, 0, 0);     // 黑色文字
$line_color = imagecolorallocate($im, 192, 192, 192); // 灰色干扰线
$noise_color = imagecolorallocate($im, 220, 220, 220); // 噪点颜色

imagefilledrectangle($im, 0, 0, 119, 39, $bg_color);

// 干扰线
for ($i = 0; $i < 3; $i++) {
    imageline($im, rand(0, 120), 0, rand(0, 120), 40, $line_color);
}

// 噪点
for ($i = 0; $i < 50; $i++) {
    imagesetpixel($im, rand(0, 120), rand(0, 40), $noise_color);
}

// 获取图片内容
$question =mc_generate_math_captcha();

// 使用SimHei.ttf字体防止图片内容乱码
imagettftext($im, 14, 0, 10, 22, $text_color, plugin_dir_path(__FILE__) . '/fonts/SimHei.ttf', $question);

imagepng($im);
imagedestroy($im);

这段代码会生成一张带干扰线的验证码图片,看起来更专业也更安全。

includes/路径下创建文件夹fonts,将SimHei.ttf字体文件放入fonts文件夹中。


🔁 第四步:刷新按钮功能

让用户能手动刷新验证码,就像换一道题再答一次:

上传一张名为 refresh.png 的刷新图片到/captcha-login/includes/路径下,注意该图片大小要适配自己验证码图片的大小,否则就不美观。

验证码登录防护

🚨 五、常见问题与避坑指南

❗ 1. Session 启动失败

  • 表现:验证码始终为空、无法保存、总是过期
  • 解决方法
  • 使用 session_start() 前不能有任何输出(包括空格)
  • 推荐挂载到 init 钩子中启动 Session
  • 可以用 @session_start() 抑制警告

❗ 2. 验证码图片空白或乱码

  • 原因
  • 文件开头有空格或 BOM 头
  • PHP的GD 库未启用
  • 图片路径不对
  • 没有适配的字体文件
  • 解决方法
  • 删除文件开头多余的空行
  • 确保服务器开启了 GD 扩展
  • 修改图片路径为正确的路径
  • 下载字体文件,通过字体文件防止乱码

❗ 3. 验证码错误或过期,提交登录后仍然可以直接登录

  • 原因
  • WP_Error不生效或者返回了 $user
  • 解决方法
  • 验证失败时用 wp_redirect() 跳转并结束脚本执行(exit

❗ 4. 验证码图片 URL 缓存问题

  • 原因
  • 浏览器缓存导致一直显示旧验证码
  • 解决方法
  • 在图片 URL 加时间戳:?t=1234567890

🧩 总结:从零到一,完成自己的安全插件!

回顾一下我们做了什么:

  1. 创建插件结构,设置插件基本信息
  2. 生成随机数学题,保存答案到 Session
  3. 动态生成验证码图片
  4. 在登录表单中插入验证码字段和图片
  5. 使用 WordPress 重写规则隐藏图片路径
  6. 在登录流程中验证用户输入
  7. 添加前端提示,让用户知道哪里出错了
  8. 清理 Session,提升安全性

每一步都像是搭积木,最终拼成了一座坚固的“安全之塔”。


🎉 结语:自己写的插件,才是最安心的盾牌

虽然整个过程有点复杂,但只要你一步步跟着做,你会发现编写 WordPress 插件并没有想象中那么难。

一旦你完成了这个验证码功能,你就掌握了 WordPress 插件开发的精髓。


📌 小贴士:开发过程中一定要做的事

  • 开启调试模式:define('WP_DEBUG', true);
  • 查看日志:/wp-content/debug.log
  • 使用浏览器开发者工具查看网络请求和表单数据
  • 经常清除浏览器缓存,尤其是验证码图片
  • 学会用 error_log("验证码不正确") 记录调试信息

By 天海牧歌

东庵每见西庵雪,下涧长流上涧泉。 半夜白云消散后,一轮明月到窗前。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注