逆向Cloudflare 5秒盾:从JS挑战到获取cf_clearance的实战指南

发布时间:2026/8/2 9:39:22
逆向Cloudflare 5秒盾:从JS挑战到获取cf_clearance的实战指南 1. 项目概述当爬虫遇到“5秒盾”做数据采集的朋友对Cloudflare的“5秒盾”一定不陌生。你精心编写的爬虫脚本满怀期待地发送请求换来的却是一个冰冷的“503 Service Temporarily Unavailable”错误或者是一个需要你手动点击“Verify you are human”按钮的页面等待那令人焦躁的5秒钟。这个机制就是Cloudflare用来区分真人用户和自动化脚本尤其是恶意爬虫的核心防线之一。它的官方名称是“Under Attack Mode”或“I‘m Under Attack”模式但在我们爬虫工程师的圈子里更习惯叫它“5秒盾”或“CF盾”。这个项目的核心目标就是彻底拆解这道防线。我们不止要绕过那个503页面更要拿到通关密钥——cf_clearance这个Cookie。有了它你的爬虫才能在后续的请求中被Cloudflare视为“已通过验证的人类”从而稳定地访问目标网站的数据。这不仅仅是一个简单的“绕过”而是一场涉及HTTP协议、JavaScript执行环境模拟、密码学挑战解答的综合性逆向工程实战。整个过程就像是在和Cloudflare的安防系统进行一场无声的智力对决。2. 逆向工程的核心思路与挑战拆解要逆向“5秒盾”我们首先得理解它的工作原理。Cloudflare并不是简单地用一个静态页面挡住你。当你首次访问一个受保护的站点时会发生以下一连串事件初始拦截与挑战下发你的请求无论是浏览器还是爬虫到达Cloudflare边缘节点。节点检测到请求特征可疑如缺少合法Cookie、User-Agent非常见、请求频率异常等不会直接将请求转发给源站而是返回一个特殊的503状态码页面。这个页面内嵌了一段高度混淆、动态生成的JavaScript代码这就是挑战的核心。客户端执行与计算浏览器或我们需要模拟的环境会执行这段JS代码。这段代码通常会做几件事收集浏览器环境指纹如WebGL、Canvas、AudioContext、字体列表、屏幕分辨率、插件信息等进行一系列复杂的数学运算可能涉及浮点数精度考验、内存操作模拟等最终生成一个答案answer。提交答案与获取通行证浏览器将计算出的answer连同收集到的一部分环境指纹数据以特定格式通常是POST请求提交给Cloudflare的一个验证端点如/cdn-cgi/challenge-platform/h/b/t/...或/cdn-cgi/challenge-platform/h/b/cv/...。验证通过与Cookie下发Cloudflare后端验证answer的正确性和环境数据的合理性。如果通过它会返回一个302重定向响应头里会包含一个Set-Cookie字段用于设置cf_clearance这个Cookie。同时它通常还会设置一个__cf_bmCookie用于Bot管理。携带通行证访问浏览器自动跟随重定向并在新的请求中自动带上刚刚获得的cf_clearanceCookie。此时Cloudflare识别到此Cookie认为你已通过人机验证于是将你的请求转发给源站服务器并返回正常的网页内容。我们的核心挑战就在于第2和第3步如何在不使用真实浏览器的情况下正确执行那段混淆的JS并生成能被Cloudflare接受的answer。这引出了几种主流思路思路一无头浏览器自动化使用Puppeteer、Playwright或Selenium等工具启动一个完整的浏览器实例如Chrome模拟真人操作点击“Verify”按钮等待JS执行完毕然后提取Cookie。这是最模拟真人、最“笨”但通常最有效的方法缺点是资源消耗大、速度慢、容易被高级指纹检测发现是自动化浏览器。思路二JS逆向与纯请求模拟这是本项目的重点也是技术含量最高的方法。核心是逆向挑战逻辑通过静态分析、动态调试理解Cloudflare下发的JS代码到底在计算什么。补环境由于Node.js或Python的默认环境与浏览器差异巨大我们需要“补全”JS代码执行时所依赖的浏览器对象和方法如window、document、navigator、location以及WebGLRenderingContext、CanvasRenderingContext2D等。生成答案在补全的环境下执行核心计算函数得到answer。模拟提交用HTTP客户端库如Python的requests模拟浏览器提交answer的请求获取cf_clearance。 这种方法速度快、资源占用低但技术难度大需要持续对抗Cloudflare的代码更新和混淆策略。思路三利用第三方库或服务有一些开源库如cloudscraper、scrapy-cloudflare-middleware或付费API服务尝试封装这部分逻辑。它们可能内部混合了上述两种方法。使用方便但可能不稳定、有使用限制或成本。本项目将深入思路二带你走通从识别503页面到获取cf_clearance的全流程理解每一个环节的细节与陷阱。3. 实战流程从503错误到获取cf_clearance3.1 第一步识别与捕获挑战页面当你用requests.get(url)遇到503时不要只看状态码。关键是要检查响应内容。import requests headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } response requests.get(https://target-protected-site.com, headersheaders) print(f状态码: {response.status_code}) # 很可能输出 503 print(f响应头: {response.headers}) print(f响应内容长度: {len(response.text)}) # 将响应内容保存到文件方便查看 with open(challenge_page.html, w, encodingutf-8) as f: f.write(response.text)打开保存的HTML文件你会看到它不是一个简单的错误提示页。页面里通常包含一个标题为“Checking your browser before accessing...”的提示。一个倒计时或“Verify you are human”按钮。最重要的是一大堆script标签里面是压缩和混淆过的JavaScript代码。其中往往有一个关键的脚本其src属性可能指向一个包含/cdn-cgi/challenge-platform/的路径或者直接内嵌了以(function(){...})()形式包裹的代码。页面中通常还隐藏着一个包含挑战参数的input标签如namejschl_vc、namepass、namejschl_answer有时是s、ray等具体名称会变。实操心得Cloudflare的挑战页面结构并非一成不变。有时挑战逻辑直接内嵌在HTML中有时是通过外部JS文件加载。第一步一定要把完整的HTML和所有关联的JS文件都保存下来因为挑战参数和逻辑可能分散在其中。3.2 第二步提取关键挑战参数与JS代码我们需要从HTML中提取出几个关键元素用于后续的请求构造和JS执行。提取挑战参数通常存在于表单或div的>from bs4 import BeautifulSoup soup BeautifulSoup(response.text, html.parser) # 查找方式可能随Cloudflare更新而变化以下是常见示例 jschl_vc soup.find(input, {name: jschl_vc}).get(value) pass_value soup.find(input, {name: pass}).get(value) # 有时参数在div的data属性里 #># 示例简单查找包含特定模式的script标签 scripts soup.find_all(script) for script in scripts: if script.string and jschl_answer in script.string: challenge_js script.string break # 如果没找到可能需要处理外部脚本注意事项Cloudflare的混淆技术很强代码可能被“压扁”移除空格换行、变量名被混淆、控制流被平坦化。直接阅读几乎不可能。我们需要借助工具进行“反混淆”或直接动态执行。3.3 第三步逆向与执行挑战JS逻辑拿到混淆的JS代码后目标是在我们的Python或Node.js环境中计算出正确的jschl_answer值。方法A使用Node.js环境执行推荐这是目前相对稳定和主流的方法。我们可以在Python中调用Node.js来执行这段浏览器环境的JS。搭建Node.js执行环境确保系统安装了Node.js。我们将使用pyexecjs或node_vm2通过subprocess调用来桥接。补环境这是成败的关键。直接将混淆的JS丢给Node.js执行肯定会报错因为Node.js没有window、document、location等浏览器对象。我们需要在执行前向JS上下文中注入这些对象的模拟实现。import execjs import json # 1. 读取我们提取出的混淆JS代码 with open(challenge.js, r, encodingutf-8) as f: obfuscated_js f.read() # 2. 构建一个补丁环境的前置代码 # 这里是一个极简的示例实际需要补的全得多包括navigator.userAgent, screen, document.cookie等 env_patch const window this; window.document { getElementById: function(id) { return { value: }; }, createElement: function() { return {}; }, // ... 其他必要属性和方法 }; window.location { hostname: target-protected-site.com, // ... }; // 补全navigator, screen, WebGL, Canvas等 // 这是一个持续对抗的过程Cloudflare会检测越来越多的指纹。 # 3. 将补丁和挑战代码拼接 full_js env_patch obfuscated_js # 4. 执行并提取答案 # 通常原JS代码最后会将计算结果赋值给某个变量如a.value我们需要修改代码让它返回这个值。 # 假设我们通过分析知道答案最终在变量 answer 中 extract_code full_js \nreturn a.value; // 或 return answer; 具体看原代码逻辑 try: ctx execjs.compile(extract_code) jschl_answer ctx.call() # 如果代码是自执行函数直接调用 # 或者 ctx.eval(...) print(f计算出的答案: {jschl_answer}) except Exception as e: print(fJS执行错误: {e})补环境的深度简单的补环境可能只能应对基础挑战。Cloudflare的“5秒盾”有不同等级。高级挑战会深度检测Canvas指纹调用CanvasRenderingContext2D的API绘制文字或图形然后toDataURL()获取哈希。你需要模拟实现这些API并返回与真实浏览器一致的结果。WebGL指纹检测WebGL渲染器和供应商字符串。字体枚举通过document.fonts或span的offsetWidth检测已安装字体。音频上下文指纹AudioContext的createOscillator和createDynamicsCompressor等。浏览器特性如Notification,Permissions,WebRTC等。 这就像一场“军备竞赛”你需要一个越来越完善的“浏览器环境模拟器”。一些开源项目如puppeteer-extra-plugin-stealth的思路就值得借鉴但我们需要在Node.js的vm环境中实现类似功能。方法B纯Python解析与计算高阶对于某些特定时期、特定形式的挑战其JS逻辑可能只是进行一系列固定的算术运算如a 12345; b a 18; c b * 2; ... answer c len(hostname)。通过仔细逆向你可以用Python重写这个计算过程。但这需要极高的逆向技巧且一旦Cloudflare更换算法就失效通用性差。3.4 第四步构造验证请求并获取Cookie计算出jschl_answer后我们还需要处理一个细节Cloudflare经常要求answer加上当前域名hostname的长度。所以最终提交的答案可能是final_answer jschl_answer len(“target-protected-site.com”)。接下来构造POST请求提交答案。这个请求的URL、参数名和格式需要从最初的挑战页面中分析得出。import time import requests # 假设我们从页面中提取了以下信息 challenge_url “https://target-protected-site.com/cdn-cgi/challenge-platform/h/b/cv/1234567890abcdef” # 这个URL需要从JS或页面中分析获取 jschl_vc “extracted_vc_value” pass_value “extracted_pass_value” ray_id “extracted_ray_id” hostname “target-protected-site.com” # 计算最终答案 (示例具体逻辑看JS) final_answer float(jschl_answer) len(hostname) # 注意可能是浮点数 # 构造提交参数 payload { ‘jschl_vc’: jschl_vc, ‘pass’: pass_value, ‘jschl_answer’: str(final_answer), # 有时需要保留多位小数 # 可能还有其他参数如 ‘r’ } headers { ‘User-Agent’: ‘Mozilla/5.0 ...‘, ‘Referer’: response.url, # 引用页是之前的503页面 ‘Content-Type’: ‘application/x-www-form-urlencoded’, } # **关键模拟浏览器等待时间** # Cloudflare的JS中通常有 setTimeout(function(){...}, 4000)要求计算后等待几秒再提交。 # 不等待或等待时间不对都会导致验证失败。 wait_time 4 # 具体时间从JS中的setTimeout参数获取 time.sleep(wait_time) # 发送验证请求 verify_response requests.post(challenge_url, datapayload, headersheaders, allow_redirectsFalse) # 注意不要自动重定向 print(f”验证响应状态码: {verify_response.status_code}“) print(f”验证响应头: {verify_response.headers}“) # 如果成功响应状态码通常是302并且在响应头中会有Set-Cookie if ‘set-cookie’ in verify_response.headers: cookies verify_response.headers[‘set-cookie’] print(f”获取到的Cookie: {cookies}“) # 从Cookie字符串中提取出cf_clearance的值 # 通常格式是cf_clearanceabc123...; expires...; path/; ...重要提示allow_redirectsFalse至关重要。我们需要手动处理302重定向因为我们要从第一个验证响应的头部获取Set-Cookie。如果允许自动重定向requests会跟随重定向发起新请求但那个新请求可能不会包含cf_clearance的Set-Cookie头因为浏览器会自动管理Cookie但我们的requests会话还没拿到这个Cookie。3.5 第五步使用cf_clearance访问目标内容拿到cf_clearance后将其加入到会话Session的Cookie中然后重新访问最初的目标URL。# 创建一个会话保持Cookie session requests.Session() # 解析Set-Cookie头将其添加到会话中 # 简单处理假设我们只关心cf_clearance import re match re.search(r’cf_clearance([^;])‘, cookies) if match: cf_clearance_value match.group(1) session.cookies.set(‘cf_clearance’, cf_clearance_value, domain’.target-protected-site.com’) # 通常也需要设置 __cf_bm # match_bm re.search(r’__cf_bm([^;])‘, cookies) # ... # 用携带了合法Cookie的会话访问原URL final_response session.get(‘https://target-protected-site.com’, headersheaders) print(f”最终访问状态码: {final_response.status_code}“) if final_response.status_code 200: print(“成功绕过5秒盾”) # 现在可以正常解析final_response.text获取数据了 else: print(“绕过失败可能原因Cookie无效、环境检测未通过、挑战已更新。”)4. 常见问题、排查技巧与进阶对抗4.1 为什么我的JS执行总是报错或答案错误这是最常见的问题原因可能有多层环境补全不充分最主要原因症状执行时抛出ReferenceError: window is not defined或TypeError: Cannot read property ‘getContext’ of undefined。排查仔细阅读错误栈看是哪个浏览器特有的API未定义。使用浏览器开发者工具在真实浏览器中触发挑战在Console中查看window、document、navigator等对象的具体结构和方法然后在你的补环境代码中逐一模拟。重点关注navigator.userAgent、navigator.plugins、navigator.languages、screen.width/height、document.documentElement.clientWidth/Height。这些是基础指纹。挑战JS代码被动态修改症状你提取的JS代码执行后计算结果与浏览器中运行的结果不一致。排查Cloudflare的JS可能包含“反调试”或“环境检测”代码。例如它可能检查Function.prototype.toString的输出、检查Error对象的栈轨迹长度来判断是否在Node.js等非浏览器环境中运行。如果检测到它会动态修改核心计算逻辑导致你算出的答案错误。你需要逆向并绕过这些检测代码或者在补环境时更彻底地模拟例如重写Function.prototype.toString返回浏览器环境下的值。答案提交格式或时机不对症状提交答案后返回非302状态或返回的Cookie无效。排查等待时间确认你模拟的等待时间time.sleep是否与JS中的setTimeout延迟完全一致。有时是毫秒数需要除以1000。答案精度jschl_answer可能是浮点数需要保留足够的小数位如15位直接str()转换可能丢失精度导致验证失败。请求参数检查POST请求的URL和所有参数名jschl_vc,pass,jschl_answer,r,s等是否与当前挑战页面中的完全一致。Cloudflare会不定期更换参数名。请求头检查Referer、Origin、Content-Type等请求头是否与浏览器行为一致。有时缺少Origin头会导致失败。IP或会话被标记症状即使流程完全正确也频繁失败。排查你的服务器IP可能已经被Cloudflare列入高风险名单触发更严格的验证如Captcha图形验证码。或者你的请求会话由初始请求携带的Cookie如__cf_bm关联已被标记为异常。尝试更换IP地址或确保整个流程从首次503请求到提交答案使用同一个requests.Session()以维持会话状态。4.2 如何应对更高级的挑战如Canvas指纹当基础算术挑战无法通过时Cloudflare可能会下发包含环境检测的挑战。这时你需要一个强大的“补环境”库。策略不要从零开始造轮子。可以研究或借鉴一些成熟项目的思路Node.js VM 环境补全寻找专门为Cloudflare反爬设计的Node.js库它们通常提供了一个高度模拟的浏览器环境。分离执行将最核心、依赖浏览器环境最多的计算部分通过一个无头浏览器如Puppeteer来执行只获取计算结果其他通信仍用requests。这是一种混合方案。指纹统一确保你补的环境指纹User-Agent, Accept-Language, Screen Resolution等在所有请求中保持一致。不一致的指纹是强大的检测信号。4.3 自动化与维护的考量逆向“5秒盾”不是一个一劳永逸的方案。Cloudflare会持续更新其挑战机制和检测脚本。监控与告警在你的爬虫系统中对503状态码和cf_clearance获取失败建立监控。一旦失败率升高立即告警。动态解析你的代码不能硬编码参数名如jschl_vc和JS代码提取规则。必须编写健壮的解析器能适应HTML结构的变化。备用方案始终准备一个备用方案例如当纯请求模拟失败时自动降级到使用无头浏览器方案虽然慢但成功率高。尊重robots.txt与速率限制即使你成功绕过了验证也应遵守目标网站的robots.txt协议并设置合理的请求间隔如time.sleep(2)。过度频繁的请求即使有cf_clearance也可能触发其他风控规则导致IP被封锁。整个逆向过程本质上是对Cloudflare安全模型的理解与对抗。它考验的不仅是编程和逆向技术更是耐心、细致和对HTTP/浏览器细节的把握。每一次成功的绕过都是对这些细节一次更深层次的掌握。记住我们的目标不是攻击而是在合理的范围内让自动化的数据采集工作得以继续。