免责声明: 本文所有分析均基于公开可访问的前端 JS 代码及 360 天御官网演示页(https://tianyu.360.cn/#/global/details/sliding-puzzle),仅用于安全研究、学习与了解验证码防护机制。文中所有接口地址来自官方公开演示环境,不涉及任何第三方业务系统。请勿将本文技术用于任何未授权的系统,违者后果自负。
一、背景介绍
360 天御是奇虎 360 旗下的人机验证方案,本文以滑动拼图验证为研究对象,分析其前端安全机制。
基本交互逻辑:
- 前端携带
appId等设备信息请求auth接口,获取背景图(bg)、滑块图(front)、会话标识(token) - 背景图以列乱序方式传输,需依据图片 ID 计算还原顺序后重组
- 使用计算机视觉识别滑块缺口位置,得到
distance - 生成拟人化滑动轨迹,经 RSA 分块加密 后构造
report参数 - 携带
report、length等参数提交check接口,服务端返回result: true/false
研究核心难点:
- 图片列乱序还原(基于图片 ID 的置换算法)
- 多策略融合缺口识别(OpenCV 四种策略 + 置信度聚类)
- RSA 分块加密轨迹数据(自定义分块边界 + PKCS1_v1_5)
二、整体流程图
生成 timestamp / nonce(本地毫秒时间戳)
↓
auth 接口(POST /api/v3/auth)
↓ 返回 bg URL、front URL、captchaId、token
下载背景图(乱序)+ 滑块图
↓
根据图片 ID 计算列置换表 → 重组背景图
↓
OpenCV 四策略融合识别缺口距离 distance(原图坐标)
↓
换算为压缩宽度坐标(300px 归一化)
↓
生成拟人化滑动轨迹 track
↓
RSA 分块加密 track → encrypted_track
report = encrypted_track + MD5(captchaId + token)
↓
check 接口(POST /api/v3/check)→ result: true/false
三、抓包分析
3.1 auth 接口
请求:
POST https://captcha.jiagu.360.cn/api/v3/auth
Content-Type: application/x-www-form-urlencoded
关键请求参数:
| 参数 | 来源 | 说明 |
|---|---|---|
| appId | 业务方配置 | 绑定具体业务,从前端 JS 中提取,固定值 |
| type | 固定 | 1 表示滑动验证 |
| version | 固定 | SDK 版本号 |
| pn | 固定 | 业务包名,前端 JS 中可查 |
| os | 固定 | 3 表示 Web 环境 |
| sdkName | 固定 | SDK 标识 |
| timestamp | 本地生成 | 毫秒时间戳 |
| nonce | 本地生成 | 与 timestamp 相同 |
| phone | 固定 | 示例账号占位 |
| sign | 本地计算 | 各参数按字母序拼接后 MD5,详见第四节 |
响应示例:
json
{
"error": 0,
"msg": "成功",
"data": {
"captchaId": "xxx",
"token": "yyy",
"bg": ["https://xxx.360.cn/xxx/<bg_id>.png"],
"front": ["https://xxx.360.cn/xxx/<slice_id>.png"]
}
}
背景图 URL 路径末段的文件名(不含扩展名)即为用于计算列置换表的
bg_id。
3.2 check 接口
请求:
POST https://captcha.jiagu.360.cn/api/v3/check
Content-Type: application/x-www-form-urlencoded
关键请求参数:
| 参数 | 来源 | 说明 |
|---|---|---|
| captchaId | auth 响应 | 本次验证会话 ID |
| token | auth 响应 | 会话凭证,原样透传 |
| length | 本地计算 | 滑动距离(300px 归一化坐标),核心识别结果 |
| width | 固定 | 300,与服务端约定的归一化宽度 |
| version | 固定 | SDK 版本号 |
| report | 本地加密生成 | 核心参数,见第五节 |
| tracking | 固定 | [object Object](前端遗留值) |
四、签名算法(sign)
auth 请求中的 sign 字段用于防篡改校验,生成规则为:
- 将所有请求参数(除
sign本身)按参数名字母序 拼接,格式为key1value1key2value2...,无分隔符 - 对拼接字符串执行 MD5 得到 32 位小写十六进制
注意:
timestamp和nonce虽然同值,但两者均参与拼接,且按字母序n在t之前。
五、图片列乱序与还原
5.1 乱序机制
服务端下发的背景图并非正常图片,而是将原始图按 17px 宽分成 32 列后打乱顺序 拼接而成(原图尺寸 544×284px,17×32=544)。还原需要先根据 bg_id 计算列置换表。
5.2 置换表计算
置换表算法以图片 ID 字符串为输入,逐字符生成一个 0~31 的无重复排列:
对于 i in range(32):
val = ord(bg_id[i]) XOR 0x20
val = val % 32
若 val 已被占用,则 val = (val + 1) % 32(线性探测)
记录 val,标记为已用
该算法本质是一个基于字符编码的哈希置换 + 线性探测冲突解决,确保每个位置只被映射一次。
5.3 图片还原
得到置换表 col_order(长度 32 的列表)后:
- 对于原乱序图的第
i列(x = i×17),将其粘贴到还原图的第col_order[i]列位置(x = col_order[i]×17)
python
for index, target_pos in enumerate(col_order):
x = index * 17
col = bg_img.crop((x, 0, x + 17, 284))
recover_img.paste(col, (target_pos * 17, 0))
注意 :图片下载时需禁用 HTTP 缓存(添加
Cache-Control: no-cache请求头并给 URL 附加随机时间戳参数),否则 CDN 可能返回上一次请求的旧图,导致每次识别结果相同但验证均失败。
六、图像识别------多策略融合缺口检测
识别目标:在还原后的背景图上找到滑块拼图缺口的 x 坐标。
6.1 四种检测策略
| 策略 | 核心方法 | 特点 |
|---|---|---|
| edge | Canny 边缘检测 + TM_CCOEFF_NORMED 模板匹配 |
对边缘轮廓敏感,受纹理噪声影响 |
| alpha_mask | 提取滑块透明通道作为掩码后匹配 | 利用 RGBA 信息精准定位,受滑块形状影响 |
| multi_scale | 以 0.9/1.0/1.1 三个缩放比例逐一匹配取最优 | 对轻微缩放有鲁棒性,置信度相对可靠 |
| gradient | Sobel 水平梯度垂直累加,找梯度峰值列 | 不依赖滑块图,纯背景分析,精度最低 |
6.2 结果融合策略
四种策略独立执行,每个返回 (x坐标, 置信度, 策略名) 三元组,再通过以下逻辑取最优结果:
1. 若任一策略置信度 > 0.7,直接采用
2. 过滤 x < 40px 的结果(缺口不可能在最左侧)
3. 遍历两两组合,若差值 ≤ 30px 则认为是同一簇:
- 用置信度加权计算簇中心
- 取总置信度最高的簇
4. 若无聚类,排除 gradient 策略后对剩余结果做置信度加权平均
gradient策略置信度动态计算(峰值/均值比值),不再固定为 0.5,避免其以固定权重干扰聚类。
6.3 距离归一化
识别得到的 distance 为原图坐标(544px 宽度下),提交前需换算到服务端约定的 300px 宽度:
python
compressed_distance = round(distance * 300 / 544)
七、轨迹生成
check 接口要求提交滑动轨迹,格式为按序编号的字典列表:
json
[{"0": {"t": 1744812345000, "y": 200.0}}, {"1": {"t": 1744812345015, "y": 200.3}}, ...]
其中 t 为毫秒绝对时间戳,y 为鼠标纵坐标。轨迹生成采用物理加速模型模拟拟人滑动:
| 阶段 | 逻辑 |
|---|---|
| 起步阶段(0~70% 目标距离) | 加速度 1.5~3,快速提速 |
| 减速阶段(70%~终点) | 加速度 -1~0.5,模拟制动 |
| 过冲(overshoot) | 超出目标 3~8px,再缓慢回退 |
| Y 轴抖动 | 每步随机 ±1.5px(前进)、±0.3px(回退) |
| 帧间隔 | 随机 1020ms(前进)、2040ms(回退) |
八、RSA 加密(report 参数)
8.1 report 结构
report = RSA_Encrypt(str(track)) + MD5(captchaId + token)
即:轨迹字符串经 RSA 加密后的结果,直接拼接会话完整性哈希(无分隔符)。
8.2 RSA 分块加密
使用 RSA PKCS1_v1_5,公钥通过模数(hex)+ 固定指数(65537)构造,模数可通过调试 SDK JS 获取。
由于轨迹字符串远超单次 RSA 加密的最大明文长度(密钥长度 - 11 字节),需要分块加密。分块边界由 SDK 自定义算法决定:
维护累计字节数 r,逐字符累加 UTF-8 字节大小:
当 (r % 117 >= 114 或 r % 117 == 0) 且 (r - 上次分块位置 >= 114) 时切割
每块独立加密为 hex 字符串,最终所有块 hex 拼接后做 Base64 编码:
python
combined_hex = ''.join(block.hex() for block in encrypted_blocks)
encrypted_track = base64.b64encode(bytes.fromhex(combined_hex)).decode()
公钥模数(
modulus)在 SDK JS 中以 hex 字符串形式存在,建议通过 HookPKCS1_v1_5.encrypt在运行时动态确认其与当前版本的对应关系。
九、完整请求流程概述
| 步骤 | 动作 | 说明 |
|---|---|---|
| 1 | 构造 auth 参数 | 生成 timestamp/nonce,计算 sign(MD5 字母序拼接) |
| 2 | 调用 auth 接口 | 获取 captchaId、token、bg URL、front URL |
| 3 | 计算列置换表 | 取 bg URL 末段文件名,逐字符 XOR+取模+线性探测冲突解决 |
| 4 | 下载图片 | 禁用 HTTP 缓存,确保每次下载最新图片 |
| 5 | 还原背景图 | 按置换表重组 32 列(每列 17px) |
| 6 | 识别缺口 | 四策略独立执行,聚类融合取最优,换算为 300px 坐标 |
| 7 | 生成轨迹 | 物理加速模型 + 过冲 + 随机抖动 |
| 8 | 加密构造 report | RSA 分块加密轨迹(自定义切块) + MD5 会话哈希 |
| 9 | 调用 check 接口 | 携带 length、report 等参数,解析返回结果 |
限流注意:auth/check 接口对请求频率有限制,频繁请求会返回 HTTP 403。建议每次请求前随机等待 1~3 秒,遇 403 冷却 10~20 秒后再重试。
十、关键知识点总结
| 知识点 | 详情 |
|---|---|
| 图片乱序机制 | 按 17px 列宽分 32 列打乱,基于图片 ID 字符串通过 XOR+取模+线性探测生成置换表 |
| 缺口识别 | OpenCV 四策略(边缘/透明通道/多尺度/梯度)并行,聚类+置信度加权融合 |
| 距离归一化 | 原图 544px → 压缩 300px,需等比换算再提交 |
| RSA 加密 | PKCS1_v1_5,自定义分块边界(117 字节周期),分块 hex 拼接后 Base64 |
| report 结构 | Base64(RSA分块加密轨迹) + MD5(captchaId+token)(直接拼接) |
| 签名算法 | 参数字母序拼接后 MD5,无需密钥 |
| HTTP 缓存陷阱 | 图片请求必须禁用缓存,否则 CDN 可能返回旧图导致识别结果固定 |
| 轨迹格式 | [{"index": {"t": ms时间戳, "y": 纵坐标}}, ...] 的编号字典列表 |
十一、与极验 GT4 的横向对比
| 对比维度 | 360 天御 | 极验 GT4 |
|---|---|---|
| 加密算法 | RSA PKCS1_v1_5,输出 Base64 | AES-CBC + RSA,输出 hex |
| 图片保护 | 列乱序(32列×17px,基于文件名置换) | 无乱序,标准图片直接下载 |
| 参数结构 | length + report(track+MD5拼接) |
单一 w 参数(AES加密数据+RSA加密密钥) |
| 工作量证明 | 无 PoW | 有 PoW(pow_detail 动态下发,支持 bits>0 碰撞) |
| 签名机制 | 参数字母序 MD5 | 无额外签名 |
| 图像识别库 | OpenCV 多策略 | ddddocr(更简便) |
| 图片分辨率 | 原图 544px → 需归一化到 300px | 标准分辨率,无需归一化 |
| JSONP 解析 | 无,标准 JSON | 需动态定位括号解析 |
| JS 混淆程度 | 中等,关键逻辑可读 | gt4.js 基本可读 |
十二、依赖安装
bash
pip install requests pycryptodome opencv-python pillow numpy
本文技术仅供安全研究与学习,切勿用于任何未授权系统,违者后果自负。