首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自己写的验证码才最安全:WordPress登录防护指南

自己写的验证码才最安全:WordPress登录防护指南

作者头像
云惑雨问
发布2025-06-17 16:18:53
发布2025-06-17 16:18:53
3190
举报
文章被收录于专栏:技术实操技术实操

本文转自:天海博客

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

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

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

***

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

我们要做的,是在 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 安装目录:/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';

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

***

🧮 第二步:创建核心文件

在插件目录下新建一个文件夹:/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("验证码不正确") 记录调试信息

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🧩 一、我们的目标:打造一个会出题的登录门卫
  • 🛠️ 二、准备工作:你不需要是程序员,但需要知道这些事
    • ✅ 1. WordPress 插件机制简介
    • ✅ 2. PHP 基础知识(不用太深奥)
    • ✅ 3. 开发工具推荐
  • 🔁 三、验证码的工作原理:像门卫查身份证一样严谨
  • 📦 四、动手实战:手把手教你写出属于你的验证码插件
    • 🔧 第一步:创建插件目录和主文件
    • 🧮 第二步:创建核心文件
    • 🖼️ 第三步:生成验证码图片
    • 🔁 第四步:刷新按钮功能
  • 🚨 五、常见问题与避坑指南
    • ❗ 1. Session 启动失败
    • ❗ 2. 验证码图片空白或乱码
    • ❗ 3. 验证码错误或过期,提交登录后仍然可以直接登录
    • ❗ 4. 验证码图片 URL 缓存问题
  • 🧩 总结:从零到一,完成自己的安全插件!
  • 🎉 结语:自己写的插件,才是最安心的盾牌
  • 📌 小贴士:开发过程中一定要做的事
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档