ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Cloudflare人机验证原理与Turnstile接入实战:从环境检测到反爬防护

Cloudflare人机验证原理与Turnstile接入实战:从环境检测到反爬防护 每天打开任意一个用了Cloudflare的网站你大概率都见过那个蓝色勾选框或者一个写着“Verify you are human”的等待页面。这就是Cloudflare人机验证最直观的样子——用户只点了一下或者干脆什么都没点挑战就通过了。但你知道这背后到底发生了什么吗人机验证这件事早期主流方案是让用户识别扭曲文字、选红绿灯、算算术后来又出现了Google的reCAPTCHA让用户“点一下”表示自己不是机器人。但这些方案都有两个绕不开的毛病一是体验差用户得花时间配合二是隐私争议大因为传统验证码厂商会跟踪用户行为来区分人和机器。Cloudflare的做法不一样它把“人机验证”从“让用户证明自己”改成了“让浏览器证明环境”这就是我今天想认真拆一拆的东西。这篇文章适合正在用Cloudflare、被爬虫和垃圾注册折磨的站长也适合对前端安全、反爬机制感兴趣的人。我会讲清楚Cloudflare人机验证的原理、Turnstile的接入方式、安全配置调优以及几个我实际踩过的坑。1. 为什么Cloudflare要做“人机验证”这件事1.1 机器人流量不是小打小闹很多人对恶意机器人的认知停留在“爬虫抓我内容”这个层面实际上的情况远比这个复杂。拿一个二线电商站来说每天的请求里可能有40%以上不是真人它们大致分几类批量抓价格和库存的比价爬虫、扫接口的撞库脚本、注册垃圾账号的批量程序、刷评论刷点击量的水军。这些流量不仅消耗服务器带宽还在污染业务数据严重的时候能把整个站点拖垮。Cloudflare本身是CDN和WAF服务商每一秒都有大量流量从它的边缘节点经过所以它在“识别请求来自人还是程序”这件事上有天然的数据优势。它的人机验证不是单纯的验证码工具而是建立在全网威胁情报之上的一个动态门槛同一个请求从同一个IP发来在凌晨三点扫登录接口和白天正常浏览首页得到的判定完全不同。1.2 传统验证码的体验魔咒传统验证码的核心思路是“让用户完成一个对机器很难、对人很容易”的任务”。早期是扭曲字母后来是图片分类再后来Google直接让用户勾选“我不是机器人”实际上是通过追踪用户的点击行为、Cookie历史、浏览习惯来打地平。问题在于真正能拦住恶意程序的验证任务往往人也很难做体验极差用户会在注册环节流失一大片。而为了保持体验故意放低难度的验证码又很容易被打码平台用人工识别破解形同虚设。这也是Cloudflare认为“人机验证应该换个方向做”的起点。与其去设计一个更难的谜题不如让浏览器在后台完成一系列环境检测把结论告诉站点这个访问者大概率是真实用户。整个过程中用户不需要做任何事顶多点一下勾选框或者点一下“验证”。1.3 从“人答题”到“环境自证”的设计转向Cloudflare对“人机验证”的最终定义不是“你能不能答对题”而是“你的访问环境是否可信”。一个真人用户使用的正常浏览器和一个程序员用来跑脚本的无头浏览器哪怕发送的HTTP请求内容一模一样它们的底层环境也是天壤之别。浏览器指纹、TLS握手特征、鼠标移动轨迹、CPU指令集、显卡渲染结果、系统字体列表、屏幕色深、时区、语言列表、WebGL参数甚至音频处理产生的波形是噪声还是精确的零值……这些信息集合在一起基本能让服务器判断出“这是不是一个人在用一个正常的浏览器”。换句话说Cloudflare的验证机制真正的考点不是人而是浏览器本身人只是“浏览器的合格操作者”。2. Cloudflare人机验证的核心技术拆解2.1 托管挑战与JS Challenge让浏览器替用户“答题”Cloudflare最常见的人机验证形态是Managed Challenge托管挑战也叫智能挑战。触发之后Cloudflare不会直接给用户看一堆图片网格而是返回一个挑战页面页面里包含一段JavaScript脚本。浏览器会执行这段脚本完成一系列环境探测然后把结果提交给Cloudflare边缘。全部通过后Cloudflare会下发一个cf_clearanceCookie。这个Cookie就是“该浏览器在这个域名下已被验证”的凭证有效期由你在安全级别里的配置决定短则30分钟长则24小时甚至更久。值得注意的是cf_clearance是绑定整个浏览器环境的不是单纯绑定Cookie。如果你清了Cookie、换了浏览器或者用一个带着完全不同指纹的自动化工具去访问即使手动把Cookie复制过去也大概率会被重新挑战。2.2 那些你感知不到的检查项TLS指纹、IP信誉与行为指标写爬虫的人都知道光伪装请求头是远远不够的。早在HTTP请求到达应用服务器之前Cloudflare在TLS握手阶段就已经开始分析你了。TLS指纹专业术语叫JA3/JA4指纹。Chrome、Firefox、Safari在建立TLS连接时ClientHello消息里的密码套件、扩展顺序、椭圆曲线参数都有各自的特征。而最常见的Python爬虫库requests它的TLS指纹和Chrome差了十万八千里服务器在第一个数据包阶段就能标记怀疑。这也是为什么很多爬虫即使加满随机UA、伪装Cookie照样在Cloudflare面前一碰就死的原因。IP信誉同样关键。Cloudflare维护着一个庞大的威胁情报库包含过去一段时间内参与过攻击、扫描、撞库、垃圾邮件、代理池的IP地址段。如果你用的是机房IP、低信誉代理或者访问频率忽高忽低、同一秒内并发请求过多都会被直接判定为高风险。行为指标则是更细的维度比如鼠标路径是否平滑、点击间隔是否符合人类节奏、键盘输入是否有延迟波动、滚动是否自然。这些在正常用户那里是“下意识动作”在脚本程序里则会暴露出机械、精确、缺乏加速度变化的特征。2.3 无头浏览器与自动化工具的识别思路有人会说那我不请求库了我用无头浏览器总行吧Headless Chrome跑起来TLS指纹和真实Chrome几乎一样JS也能执行看起来应该很难区分。实际上无头浏览器依然有大量破绽。现代浏览器在正常运行时会有一些只存在于真实渲染流程里的特性比如WebGL上下文的GPU供应商信息、Canvas渲染出的像素噪点、字体的千分位排列、浏览器扩展在DOM中注入的节点、系统级的时间源偏差。无头模式下很多特性是缺失或异常的。再加上Chromium本身在无头模式中会暴露一个HeadlessChrome的UA标记如果被去掉又会在navigator.webdriver属性、window.chrome对象完整性、Permissions API行为上露出马脚。Cloudflare的检测不是单点信任而是综合打分。只要有一两个特征异常就会进入挑战流程如果多项异常且请求路径敏感比如登录、注册、提交表单就会直接被拦截。这套机制的可怕之处在于它是动态迭代的Cloudflare每隔几周就会悄悄加入新的检测维度写反爬工具的团队只能被动追踪很难做到长期稳定的绕过。2.4 Turnstile不依赖跨站追踪的独立人机验证2022年Cloudflare发布了Turnstile这是一个可以嵌入任意网站的独立人机验证产品类似于reCAPTCHA的全新替代品。最大的区别是它不像Google那样依赖跨站点Cookie和长期追踪来评估用户而是基于当前页面里的挑战环境实时判断更贴合隐私法规的要求。Turnstile有几种渲染模式隐形模式全程无感后台自动完成验证受管模式会显示一个挑战框但大多数情况下用户不需要点击它自动通过非侵入式模式则要求用户点一下勾选框。对站长来说Turnstile最爽的一点是它可以脱离Cloudflare CDN单独使用也就是说你的服务器可以放在任何地方网站也能接入这套人机验证。而且它不要求深度集成前后端加起来几十行代码就能跑通。3. 实操给你的网站接入Turnstile人机验证3.1 创建站点并获取Site Key与Secret Key接入Turnstile的第一步是登录Cloudflare控制台在左侧菜单找到Turnstile入口。点击“Add Site”之后需要填写站点名称和域名然后选择验证模式。官方给了三种Managed受管、Non-Interactive隐形、Invisible不可见。我通常建议首选Managed因为它在用户完全不操作时自动通过只有在风险较高时才展示交互挑战平衡体验与安全性。提交后你会得到两串关键信息这就是你的钥匙了。参数用途安全等级Site Key嵌入前端页面公开可见低Secret Key后端服务器校验Token严禁暴露高这里有一条新手最容易犯的错误把Secret Key写在JavaScript里等于把你的验证后门大敞四开。任何看到网页源码的人都能用你的密钥去伪造校验结果。Secret Key只允许放在服务器端绝对不要出现在前端代码或Git仓库里。3.2 前端接入显式渲染与隐式渲染Turnstile的前端接入有两种方式一种是用官方脚本加一个div标签让脚本自动渲染另一种是显式调用JavaScript API来控制验证框的位置和交互逻辑。先看最简洁的自动渲染方式script srchttps://challenges.cloudflare.com/turnstile/v0/api.js async defer/script div classcf-turnstile>script window.onloadTurnstileCallback function () { turnstile.render(document.getElementById(captcha-box), { sitekey: YOUR_SITE_KEY, callback: function (token) { document.getElementById(turnstile-token).value token; }, expired-callback: function () { // Token过期时需要让用户重新验证 document.getElementById(turnstile-token).value ; }, }); }; /script script srchttps://challenges.cloudflare.com/turnstile/v0/api.js?onloadonloadTurnstileCallback async defer/script input typehidden idturnstile-token nameturnstile-token value我建议你在生产环境里一定要监听expired-callback。Cloudflare下发的验证令牌有效期只有2分钟如果用户填表填到一半超时了再提交表单就会后端校验失败。监听这个回调把隐藏域清空并且在提交时检查Token是否为空能避免大量莫名其妙的报错。3.3 后端校验siteverify接口与返回参数解读前端获得的Token本身不代表验证通过它只是一张“待兑奖券”真正的判定必须由后端调用Cloudflare接口完成。也就是说黑客可以绕过前端直接POST但拿不到合法的Token后端校验这关就过不去。后端请求地址是固定的curl -X POST https://challenges.cloudflare.com/turnstile/v0/siteverify \ -H Content-Type: application/x-www-form-urlencoded \ --data secretYOUR_SECRET_KEYresponse用户提交的TOKENremoteip用户访问IP返回结果是一个JSON对象正常情况下是这样的{ success: true, challenge_ts: 2024-06-12T10:30:00.000Z, hostname: example.com, error-codes: [] }有几个点需要提醒你。返回值里有hostname字段那是生成Token的站点域名后端一定要校验它和你站点的域名一致防止攻击者拿到别人站点的合法Token来提交你的表单。remoteip参数建议传这样Cloudflare可以校验Token和IP是否绑定但注意如果用户的IP是动态变化的比如移动网络你需要在生成页面时存好一个会话里的IP保证前后端判定一致。常见的error-codes我记得有这么几种missing-input-secret表示未传Secret Keyinvalid-input-secret表示Secret Key格式错误missing-input-response表示没收到前端Tokeninvalid-input-response表示Token不存在、已过期或已被使用过bad-json表示请求体不是合法的JSON格式。排查问题的时候先对照这个表判断阶段比瞎猜快得多。3.4 进阶防护结合Token时效、Double-Submit Cookie与重复校验很多人以为加了Turnstile就万事大吉了其实不是。Token只能证明“这个浏览器在你接入了Turnstile的页面上通过了验证”不能证明“这次提交表单的这个会话没有被劫持”。建议把Token校验和会话机制配合起来。一个简单但有效的组合方案是Double-Submit Cookie登录或进入表单页时后端生成一个随机值写入HttpOnly Cookie前端提交表单时把这个随机值放在隐藏字段里后端校验隐藏字段的值和Cookie里的值是否一致。再加上Turnstile Token就形成了“密码学Token 会话绑定 前端环境验证”三道防线。对于普通业务站点做到这个程度已经能挡住绝大多数恶意提交了。另外一个容易漏的点是Turnstile Token是一次性的用过后立即失效。也就是说你的后端接口必须保证Token只被校验一次并且校验完成后要把Token记录到临时存储或加个缓存标记。如果不做这步攻击者可以在自己页面上先获取一个合法Token然后批量重放你的提交接口照样能把你的服务打崩。4. 在Cloudflare站点上配置验证策略4.1 Security Level 与 Under Attack Mode 的联动接入Cloudflare CDN之后人机验证不一定要靠Turnstile单独做Cloudflare本身就能在边缘层替你拦截恶意流量。Security Level安全级别是最基础的设置它决定了一个访客在触发什么条件时会被要求完成托管挑战。五个档位的逻辑是这样的基本上所有人都信任的IP比如搜索引擎爬虫、企业IP段在Even with low风险下也不挑战而对于机房和高风险IP在Medium档就会触发挑战。我实际配置的经验是有论坛、评论区、注册页的站点建议调增强档也就是在需要时对可疑IP直接加验证如果站点同时开了Turnstile做业务层验证边缘层的安全级别可以适当放宽避免同一用户被验证两次降低体验损失。Under Attack Mode攻击模式则是另一个极端。开启后Cloudflare会给所有访客显示一个等待挑战页面做5秒的JavaScript计算通过后再进入站点。这个模式只建议在站点正遭受大规模攻击时临时开启因为连搜索引擎爬虫也会被拦对自然流量伤害非常大。我见过有人把攻击模式当成常驻设置结果站点搜索收录量掉了一半得不偿失。4.2 Bot Fight Mode 与托管挑战怎么搭配Bot Fight Mode是Cloudflare提供的另一层机器人防护在Dashboard的Bots菜单里开启。免费版和Pro版提供的是简化版逻辑主要针对请求特征明显的非浏览器流量比如缺UA的脚本、请求频率异常的IP。Bot Fight Mode和管理挑战机制是不冲突的Bot Fight Mode负责“击落”托管挑战负责“筛选”。合理的搭配策略是对前端页面路径比如首页、文章页开启Bot Fight Mode把明显的机器人流量直接拦截对登录、注册、提交表单的业务路径配置WAF自定义规则用Managed Challenge而不是直接拦截因为误杀真人的损失远大于放过一两个机器人。Cloudflare的WAF自定义规则支持Managed Challenge动作你可以非常灵活地指定哪些Path、哪些国家、哪些IP段需要挑战。4.3 防止误伤真实用户的几条调优原则人机验证最大的坑不是拦不住机器人而是把真人挡在门外。我在调优过程中总结出几条原则基本能避开绝大多数误伤。第一给真正的爬虫开会后门不可取但给搜索引擎放行是必须的。Googlebot、Bingbot这些搜索引擎爬虫有公开的DNS反向解析和IP段Cloudflare默认会识别但最好再写一条WAF规则明确放行否则站点内容收录会出问题。第二对低频访问的新用户不要一上来就用高强度验证。把安全级别设为Medium优先挑战高风险IP而不是挑战所有IP。很多真实用户只是换了个网络环境比如刚从办公楼切换到公共WiFiIP信誉不高但行为正常又正好赶上了高安全级别的站点就会被无端验证。第三记录防火墙事件日志。Cloudflare的Security Events面板能看到每个被拦截或挑战的请求的详细特征包括IP、ASN、路径、是哪个规则触发的。定期看这个日志把被频繁误伤的IP段加入白名单或者把产生误伤的规则改得更精准是长期维持良好体验的唯一办法。5. 常见问题排查与避坑实录5.1 页面一直转圈、验证总是不通过的排查顺序这是被问得最多的问题用户反馈“我明明是真人在操作验证框却一直转圈或者点击验证后没反应”。这种问题我建议按下面的顺序排查。首先看浏览器系统时间是否正确。Cloudflare的盾牌验证依赖时间戳计算如果本机时间比服务器慢几分钟挑战脚本生成的凭证就是无效的。这个问题在重装系统、电脑休眠过久、主板电池没电的机器上最常见。其次看Cookie状态挑战成功需要写入cf_clearanceCookie如果浏览器禁用了第三方Cookie或者开了严格隐私模式验证很难完成。然后是浏览器扩展广告拦截类和隐私保护类扩展经常会把挑战脚本的请求拦掉或改掉导致验证死循环。实测下来开启“严格模式”的uBlock和某些翻译插件都有这种问题。如果你做的是Turnstile接入一直转圈还要额外检查是否引入了多个版本的api.js脚本或者重复调用了turnstile.render。同一页面只能有一个Turnstile实例正常工作重复渲染会导致第二个验证框永远等不到回调。5.2 真实用户被误拦怎么办人机验证误拦真人有两类场景一类是站点安全级别设置得太激进另一类是用户的网络环境被Cloudflare标记为高风险。如果是第一类处理方式很简单在Security Level里把档位降一档或者在WAF规则里针对被误伤的路径放行。如果是第二类就麻烦一点因为问题出在用户侧不是你的配置。常见的用户侧原因有用户用的是机房IP或共享出口IP这个IP之前被用作攻击源或爬虫池用户的网络网关设备被植入恶意插件产生了大量可疑流量用户的本地DNS被污染导致请求头里出现异常。这时候你可以在Cloudflare的Security Events里把该用户的IP拉出来看如果确认是共享IP导致的误判可以添加一条Country或ASN级别的信任规则把这家运营商的相关IP段放行。但要注意不要放得太宽否则机器人也会钻空子。5.3 “找不到电子邮件路由”报错排查这个报错是我最近帮一个朋友排查时遇到的他在Cloudflare控制台想给自己的域名启用Email Routing结果后台提示“找不到电子邮件路由”同时他在网站后台怎么也收不到管理员验证邮件。乍一看和人机验证没关系但把链路追下来你会发现它们经常卡在同一个环节。Cloudflare Email Routing是一个邮件转发服务它需要你确认域名所有权并创建相应的DNS记录才能激活。激活的标准动作是在Email Routing页面里点击“Start setup”然后按要求在DNS页面添加MX记录和TXT记录。如果没有正确添加或者添加后没有完成验证控制台就会显示找不到电子邮件路由。排查时首先看DNS记录确认域下的MX记录指向的是Cloudflare的route1.mx.cloudflare.net等值TXT记录里含有vspf1 include:_spf.mx.cloudflare.net之类的校验字符串。其次看域名控制权Cloudflare的邮件路由验证要求你的域名必须把DNS托管在Cloudflare上也就是说必须使用它的NameServer。如果域名换过DNS服务商或者中途调整过NS记录这个报错就会出现。还有一个隐藏原因很多人在第一步收到“We sent you a verification email”之后就干等着没注意到验证邮件被过滤了。因为Cloudflare的邮件验证是从verifycloudflare.com发出的部分企业邮箱把它们当成垃圾邮件扔进回收站你自己那里没看到后台就一直卡在“未验证”状态表现出来的就是找不到路由。处理的完整顺序是检查DNS记录是否完整、确认域名NS是否在Cloudflare、去垃圾邮件里翻验证邮件、如果实在没有就点“Resend verification email”重新发送收到点击确认后路由才会被激活这时候再去网易邮箱配置转发规则接收管理员邮件。5.4 我的几点经验随笔接触Cloudflare人机验证这几年我最深的体会是没有一套验证方案是百分之百完美的所谓的安全本质上是在“拦住机器人”和“不惹恼真人”之间找平衡。一个很现实的例子是Turnstile接入初期我为了追求“绝对安全”把校验逻辑写得特别死Token过期时间、IP绑定、Double-Submit Cookie全开了结果线上马上出现一批老用户投诉说表单提交老失败。后来查下来大部分失败发生在用户填写时间超过2分钟、Token过期但页面没有自动刷新验证的场景。后来我把expired-callback的刷新逻辑补全并且在Token过期时自动调用turnstile.reset()投诉量立刻降为零。细节这种东西真的是要用线上事故来换。还有一点想多说一句Cloudflare的挑战机制和互联网的攻防博弈一样是动态演进的。今天管用的检测维度明天可能就被绕过今天好用的拦截策略过两个月可能误伤一片人。所以别把安全配置调完就彻底不管了定期去Security Events面板翻一翻看看最近被人机验证挡住的请求里有多少是真的可疑流量、有多少是误伤。数据会告诉你你的配置到底是不是合理。如果你正在给自己的站点接人机验证建议先开Turnstile试水再逐步调整Cloudflare边缘层的挑战策略。这个组合是目前我试过的里面对真实用户最友好、对机器人最不客气的一套方案。
返回列表