腾讯天御 TenDI 防水墙
腾讯天御 TenDI 防水墙 是 Tencent Cloud 的弹出式 / 嵌入式行为验证码 —— 文字与图标点选、滑块拼图,风控可信时走智能免验证(不出题)。页面弹出一个带腾讯云标识的验证浮层,最常见是「请依次点击」几个汉字或图标,其次是把碎片拖到缺口的滑块拼图。被判定为可信用户时一道题都不出,浮层一闪而过就通过(智能免验证 / 无感验证)。
也叫 TenDI(腾讯天御的英文名)。调用时 type 一律传 tencent_tendi。
接口规格
| 项目 | 值 |
|---|---|
| type | tencent_tendi |
| 必填参数 | website_urlwebsite_key |
| 可选参数 | captcha_script |
| 返回 | 一组字段(要一起提交)(solution.ticketsolution.randstr) |
| 怎么用这个解 | 两个字段一起回填目标站的校验接口 |
| 价格 | $2.50 / 1K 次 —— 按成功计费,没解出来退款 |
| 厂商文档 | Tencent Cloud 官方文档 |
参数去页面哪里取
下面全部是可以直接照做的定位方法 —— DOM 属性名、JS 全局变量名、script 上的 query 参数。
我们要两个:website_url = 出现验证的那个页面地址;website_key = 天御的 CaptchaAppId(9–10 位纯数字,官方文档示例 199999164,公开演示站是 2000000008)。三条取法,从最准到兜底,直接粘进控制台:
// ① 从已发出的验证码请求里取 aid —— 最准,开了 aidEncrypted 时也只有这条能用
performance.getEntriesByType('resource').map(e=>e.name)
.filter(n=>/t\.captcha\.qq\.com|captcha\.qcloud\.com/.test(n))
.map(n=>{try{return new URL(n).searchParams.get('aid')}catch(e){return null}}).filter(Boolean)
// ② 验证浮层已经弹出来时,直接读 iframe
document.querySelector('iframe[src*="t.captcha.qq.com"]')?.src
// ③ 兜底:扫页面源码里的 CaptchaAppId / aid
document.documentElement.innerHTML.match(/(?:CaptchaAppId|aid)["'\s:=]{1,4}["']?(\d{9,10})/i)
// 顺带确认脚本地址(我们的可选参数 captcha_script 就填它,非默认时才需要)
[...document.scripts].map(s=>s.src).find(s=>/captcha\.qcloud\.com\/T\w*Captcha\.js/.test(s))拿到解之后:我们返回 ticket 与 randstr 两个字段,一起填回目标站的校验接口(多数站点表单字段就叫 ticket / randstr),只填一个必然不过。
调用示例
一个端点解全部验证码类型 —— 换一种验证码只改 type 这一个字段。
curl -X POST https://api.everyinfra.com/api/v1/captcha \ -H "Authorization: Bearer omg_你的KEY" \ -H "Content-Type: application/json" \ -d '{"type":"tencent_tendi","website_url":"<website_url>","website_key":"<website_key>"}'
{ "solution": { "ticket": "…", "randstr": "…" }, "billing": { "charged": true, "credits": 180 }}
怎么确认目标站用的就是它
页面动态引入 `https://turing.captcha.qcloud.com/TCaptcha.js`(2024-11 起的新版是同域的 `TJCaptcha.js`,官方要求动态引入、不许本地化),然后 `new TencentCaptcha(CaptchaAppId, callback, options)` 实例化;嵌入式写法把首参换成容器元素并传 `{type:'embed'}`。题面在 `t.captcha.qq.com` 的 iframe 里完成,会话由 `cap_union_prehandle` 起。通过后回调对象给 `ret=0`、`ticket`、`randstr`(还有 `appid`、`errorCode`、`errorMessage`、`sid`、`verifyDuration`)。业务前端把 `ticket` + `randstr` 随表单交给自己的服务端,服务端用 `CaptchaAppId` + `AppSecretKey` 调 `DescribeCaptchaResult`(Version `2019-07-22`,`CaptchaType` 固定 `9`),返回 `CaptchaCode=1` 才算通过。`ticket` 有效期 5 分钟且只能用一次(复用报 `9 ticket reused`,过期报 `8 ticket expired`)。
容易搞混的
别和 QQ 老登录链路 ptlogin2 的字符验证码混(那是另一代产品);也别和「腾讯云 T-Sec 天御」名下的业务风控、验证码短信等同产品线其它模块混 —— 这里说的只是行为式验证码 Captcha。
常见部署场景
- 微信生态:公众平台、小程序管理后台、部分 H5 授权页
- QQ 系产品与腾讯云自身控制台的登录、改密
- 大量国内 SaaS / 政企 / 电商站点的注册登录、短信下发、找回密码
- 活动秒杀、点赞发帖、表单提交等防刷场景(这是天御主打的四类场景)
已知的坑
回调对象里的字段是全小写 appid,和请求侧的 CaptchaAppId 不同名 —— 这不是笔误,是腾讯请求侧与回调侧本来就不一致;照请求侧拼写去取永远取不到。而且它只是入参回显,没有信息量。
JS 加载或初始化失败时 SDK 会自造容灾票据,格式是 trerror_ 前缀(例如 trerror_1001_<appid>_<时间戳>),randstr 是 @ + 随机串。那不是真票,服务端核查会返回 CaptchaCode=21 diff。
AppSecretKey 是服务端密钥,绝不会出现在前端;求解只需要 CaptchaAppId。页面上能看到 AppSecretKey 说明目标站配错了。
官方建议开启 aidEncrypted(AppId 鉴权加密串),开了之后页面源码里不再有明文 AppId,只能从 cap_union_prehandle 请求的 aid 参数里取。
ticket 5 分钟过期、一次性:拿到就用,别缓存、别重放。
官方明确建议按 ticket 与 errorCode 分支处理而不是按 ret,因为容灾路径下 ret 不可靠。
website_key 传的是天御的 captchaAppId(形如 189123456)
常见问题
腾讯天御 TenDI 防水墙 要传哪些参数?
必填 website_url、website_key;可选 captcha_script。请求里 type 传 tencent_tendi。
解出来的东西怎么用?
返回一组字段(要一起提交),取 solution.ticket、solution.randstr。两个字段一起回填目标站的校验接口
CaptchaAppId 要去页面的哪里找?
重点找 CaptchaAppId、randstr、ticket。本页「参数去页面哪里取」一节写了全部位置与取法,含可直接粘进控制台的一行命令。
怎么确认目标站用的就是 腾讯天御 TenDI 防水墙?
别和 QQ 老登录链路 ptlogin2 的字符验证码混(那是另一代产品);也别和「腾讯云 T-Sec 天御」名下的业务风控、验证码短信等同产品线其它模块混 —— 这里说的只是行为式验证码 Captcha。
有什么容易踩的坑?
回调对象里的字段是全小写 appid,和请求侧的 CaptchaAppId 不同名 —— 这不是笔误,是腾讯请求侧与回调侧本来就不一致;照请求侧拼写去取永远取不到。而且它只是入参回显,没有信息量。
多少钱一次,失败扣不扣?
$2.50 / 1K 次。按成功计费 —— 没解出来一律退款,所以失败不花钱。账单按次记,标价按每千次是为了让量级读得出来。