• li3295
    极验验证
    ID slide_captcha版本 2.0.2插件制作者 li3295售价 5 积分830 行 / 35.4 KBHook / 路由 / 后台页
    接入极验行为验证第四代(GT4),用于注册、发帖、回帖校验;可限制换 IP / 设备重复注册。
    收费插件安装说明购买后点击“授权码”获取一次性授权码;在后台 > 插件广场点击在线安装或更新,并填写该授权码。已购买用户更新免费:每次在线安装或更新前,重新点击“授权码”获取新的一次性授权码即可,无需再次购买;也可直接下载新版后用后台“插件上传”更新。尊重并感谢他人劳动,请勿传播代码。
    app/plugins/slide_captcha/plugin.php
    开发日志已有 4 条

    此项目接受所有登录用户协助维护和提交更新。

    • li3295
      li3295
      版本 2.0.2 3511f3e81443 更新:更新了release
    • li3295
      li3295
      版本 1.4.4 2c23a34352f0 更新:优化bug
    • li3295
      li3295
      版本 1.3.0 7c22517dbe24 更新:2.0更新限制同一网段(IPv4 /24,防换邻近 IP) 限制同一设备指纹(防清 Cookie 换 IP)
    • li3295
      li3295
      版本 1.2.0 a67aceefd021 更新:放灌水 后台可以设置开启关闭 注册、发帖和回帖前必须完成滑动验证,无法跳过;并可限制同一 IP 与浏览器只能注册一个账号。
    主楼
  • 质量报告

    插件质量报告

    插件:slide_captcha

    Hook功能范围循环使用频率文件读写修改系统表读写系统表读写自己的表
    page.before_render全站页面输出前否5----
    page.head全站页面 head否5----
    register.form_extra注册表单否3----
    reply.before_save回帖发布/编辑表单提交回调内3读写 修正❗️---
    topic.before_save主题发布/编辑表单提交回调内3读写 修正❗️---
    user.after_save用户注册/资料保存后否3---写 plugin_slide_captcha_reg_marks
    user.before_save用户注册/资料保存前回调内3读写 修正❗️--读 plugin_slide_captcha_reg_marks
    reply.form_extra回帖编辑表单否2----
    topic.form_extra主题发布/编辑表单否2----

    颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。

    数据字典

    plugin_slide_captcha_reg_marks

    字段类型可空默认值约束
    idid否-主键、自增
    kindkey否-唯一
    mark_keykey否-唯一
    user_iduint否0-
    created_atuint否--
    #1
  • li3295

    版本 1.2.0 更新:
    放灌水 后台可以设置开启关闭 注册、发帖和回帖前必须完成滑动验证,无法跳过;并可限制同一 IP 与浏览器只能注册一个账号。

    #2
  • bbs1org

    这个安全性如何?能防住机器人吗

    #3
  • li3295

    可以的 就是安全性提供安全性了 可以防住机器人

    #4
  • mingmeo

    注:以下仅适用于 滑块验证 本身而并不包括其余限制措施。

    该验证存在以下问题

    1. 服务端仅验证数据,且过度相信客户端数据:
    percent = 100; // 直接 100%
    duration = 800; // 合法区间
    moves = 10; // 合法区间

    使用此数据发送请求即可直接通过。

    #7
  • li3295

    优化你说的bug了 你再去测一下

    #13
  • mingmeo

    存在严重问题:前端丢弃了收集的拖动信息,在松开后重新获取了令牌,并提交了

    [0, 16, 32, 48, 64, 80]

    这组预设数据,导致行为验证完全失效。

    同时,这也暴露出了一个问题:预制的机器数据可以通过验证!

    您可复制以下脚本,粘贴至浏览器控制台执行。

    (async function bypassSlideCaptcha() {
        const widgets = document.querySelectorAll('[data-slide-captcha]');
        if (!widgets.length) {
            console.error('未找到滑动验证组件');
            return;
        }
        for (const root of widgets) {
            const scene = root.getAttribute('data-slide-captcha-scene') || 'register';
            const challengeUrl = root.getAttribute('data-challenge-url');
            const tickUrl = root.getAttribute('data-tick-url');
            const verifyUrl = root.getAttribute('data-verify-url');
            if (!challengeUrl || !tickUrl || !verifyUrl) {
                console.warn('滑块缺少必要URL,跳过');
                continue;
            }
            const csrf = document.querySelector('input[name="_csrf"]')?.value || '';
            const post = async (url, bodyObj) => {
                const formData = new FormData();
                for (const [k, v] of Object.entries(bodyObj)) {
                    formData.append(k, v);
                }
                if (csrf) formData.append('_csrf', csrf);
                const res = await fetch(url, {
                    method: 'POST',
                    body: formData,
                    credentials: 'same-origin',
                    headers: { 'X-Requested-With': 'XMLHttpRequest' }
                });
                return await res.json();
            };
            const challengeResp = await fetch(
                challengeUrl + (challengeUrl.includes('?') ? '&' : '?') + 'scene=' + encodeURIComponent(scene),
                {
                    credentials: 'same-origin',
                    headers: { 'X-Requested-With': 'XMLHttpRequest' }
                }
            );
            const challengeData = await challengeResp.json();
            if (!challengeData || !challengeData.ok || !challengeData.token) {
                console.error('获取挑战失败', challengeData);
                continue;
            }
            const token = challengeData.token;
            console.log(`[${scene}] token:`, token);
            const points = [0, 16, 32, 48, 64, 80];
            let prevPercent = -1;
            for (let i = 0; i < points.length; i++) {
                const p = points[i];
                if (p <= prevPercent) continue;
                const delay = i === 0 ? 30 : 75;
                await new Promise(r => setTimeout(r, delay));
                const tickData = await post(tickUrl, { token, percent: String(p) });
                if (!tickData || !tickData.ok) {
                    console.error('tick 失败', tickData);
                    throw new Error('tick 失败');
                }
                prevPercent = p;
            }
            const verifyData = await post(verifyUrl, { token, scene });
            if (!verifyData || !verifyData.ok || !verifyData.ticket) {
                console.error('验证失败', verifyData);
                continue;
            }
            const ticket = verifyData.ticket;
            console.log(`[${scene}] ticket:`, ticket);
            const ticketInput = root.querySelector('[data-slide-captcha-ticket]');
            if (ticketInput) {
                ticketInput.value = ticket;
            }
    
            // 更新 UI,可选,不影响后端判断
            root.classList.add('is-ok');
            root.classList.remove('is-fail');
            const textEl = root.querySelector('[data-slide-captcha-text]');
            if (textEl) textEl.textContent = '验证通过';
            const progress = root.querySelector('[data-slide-captcha-progress]');
            if (progress) progress.style.width = '100%';
            const knob = root.querySelector('[data-slide-captcha-knob]');
            if (knob) {
                knob.style.left = 'auto';
                knob.style.right = '3px';
                knob.disabled = true;
            }
            const refresh = root.querySelector('[data-slide-captcha-refresh]');
            if (refresh) refresh.hidden = false;
    
            console.log(`[${scene}] 验证绕过成功`);
        }
    
        console.log('滑动验证已绕过,请尝试提交表单');
    })();
    #14
  • li3295

    算了我直接接入api验证滑动吧

    #15
  • li3295

    换了

    #21
  • 蓝太阳

    有图片吗?是什么样的滑动

    #5
  • li3295

    你可以安装看看在本地的

    #6
  • li3295

    版本 1.3.0 更新:
    2.0更新限制同一网段(IPv4 /24,防换邻近 IP)

    限制同一设备指纹(防清 Cookie 换 IP)

    #8
  • li3295

    正在优化 优化完了更新

    #9
  • 123456789

    限制ip不行。正常用户ip本身就是被动随机更换的。这个要去掉。
    @li3295

    最后由 123456789 编辑于 2026-08-15 18:03
    #10
  • li3295

    后台可以开启关闭 不是强制的

    #11
  • li3295

    版本 1.4.4 更新:
    优化bug

    #12
  • 123456789

    还不如接入极验的验证码。

    #16
  • 123456789

    自己写很难的。总是有漏洞。

    #17
  • li3295

    这就接

    #18
  • 123456789

    🤨

    #19
  • li3295

    版本 2.0.2 更新:
    更新了

    #20
  • bbs1org

    需要网站备案吗

    #22
  • li3295

    不需要

    #23

发表回复

登录后回复