尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

动态安全Cookie逆向实战:从抓包插桩到本地复现的完整思路

发布时间:2026/9/25 9:49:10

资讯中心
01
ARTICLE

动态安全Cookie逆向实战:从抓包插桩到本地复现的完整思路

动态安全Cookie逆向实战:从抓包插桩到本地复现的完整思路
最近在研究东航官网的接口自动化遇到了一个非常典型的问题用 Python 的 requests 直接带登录后的 cookie 去访问查询接口总是被风控拦下来返回 403。可同样一套 cookie 放在浏览器里一切又正常。抓包对比了几次发现问题出在两个动态 cookie 上ssxmod_itna和ssxmod_itna2。这两个值不是服务端下发后就不变的静态字段而是每次页面加载时由前端一段加密 JS 动态计算出来的。服务端收到请求后会校验这个 cookie 的合法性校验不通过就直接拒绝访问。这篇文章就记录一下我是怎么把这两个 cookie 的生成逻辑一步步拆出来的以及在实际分析中几条更省力的替代思路。适合做接口自动化、爬虫研究、前端逆向或者自己站点在做风控自查的读者参考。1. 项目背景与问题拆解1.1 这两个 cookie 到底是什么先说结论ssxmod_itna和ssxmod_itna2属于典型的动态安全 Cookie是当前不少安全防护产品都会采用的一种反自动化机制。它们和普通会话 cookie比如 JSESSIONID、SESSION完全不同。普通 cookie 的作用是维持登录状态服务端在登录成功后下发一个 token你带着它访问就行。动态 cookie 的作用则是“证明当前请求来自真实浏览器环境”。服务端会在首次响应页面 HTML 时夹带一段经过混淆的 JS 脚本。浏览器加载并执行这段脚本后脚本会结合当前环境信息时间戳、浏览器指纹、运行状态等计算出一串结果写入 cookie 中。后续凡是访问受保护接口的请求都必须携带这个 cookie服务端再对里面的信息做校验。所以这两个字段本质上是一道“动态密码门”。就算你完全登录成功没有这道门禁密码请求依然会被拦截。1.2 为什么要做算法分析遇到这种问题最原始的办法是手动打开浏览器从 DevTools 里复制 cookie 出来塞到 requests 里。问题在于这个 cookie 的有效期非常短而且经常跟着页面刷新变化。我实测过从浏览器复制出来的ssxmod_itna快的时候几分钟就失效慢的时候十几分钟。如果做批量接口测试或者定时任务这种方式完全不靠谱。要彻底解决问题有两条路把生成 cookie 的 JS 算法完整还原在本地用 Python 或 Node.js 重新实现一遍这样就能在任意脚本中生成合法 cookie。模拟浏览器执行原始 JS执行完成后再把 cookie 取出来也就是“浏览器环境 动态取 cookie”的混合方案。两条路对应不同的工作量和稳定性。这篇文章会把两条路都讲一遍重点分析第一条路的拆解思路同时给第二条路的具体落地方案。毕竟很多场景下我们需要的只是“能用、稳定”而不是真的和 JS 混淆代码死磕到底。注意本文只讨论技术原理和调试方法所有分析均应在获得授权、且用于学习研究或自有业务接口调试的前提下进行。2. 环境准备与工具选型2.1 基础运行环境做这类分析最推荐的环境是 Windows 10/11 或 macOS。内存 16GB 以上最好因为要同时跑浏览器、抓包工具和调试脚本。系统版本不建议太老否则浏览器版本被限制指纹环境容易和目标站点要求的版本差太多导致算法本身没分析错但环境参数对不上白折腾。软件环境方面我建议准备以下内容Node.js 16 以上版本用来跑 Playwright 和执行被还原出的 JS。Python 3.9 以上用来写最终的调用脚本。Chrome 浏览器正式版即可DevTools 是关键调试工具。Fiddler 或 Charles任选一个用于观察请求响应过程。一个代码编辑器推荐 VS Code配合搜索功能在 JS 文件里定位关键词很方便。2.2 核心工具清单与作用工具用途使用阶段Chrome DevTools打断点、观察调用栈、查看 cookie 变化算法定位阶段Fiddler/Charles抓取完整 HTTP 请求响应比较首次访问时的差异入口分析阶段Playwright模拟真实浏览器执行 JS稳定获取动态 cookie落地实现阶段de4js对混淆 JS 做初步还原识别主流混淆框架代码分析阶段Babel/AST 工具深度还原复杂混淆逻辑提取关键计算过程还原复现阶段这里多说一句工具选型的逻辑。很多人一上来就盯着算法本身忽略了“先确认 cookie 是在什么时机生成的”这个步骤。抓包工具在这时候的价值就是帮你快速找到生成 cookie 的那个 JS 文件。以我分析的案例来说第一次访问东航首页时返回的 HTML 里会加载一个独立的 JS 文件文件名是动态的、带一串随机字符串。这个文件里就藏着ssxmod_itna的生成代码。用 Fiddler 过滤一下 JS 资源对比“第一次访问”和“带 cookie 二次访问”的响应差异基本能一眼锁定目标文件这一步走对了后面可以节省大量时间。3. 核心细节解析与原理拆解3.1 cookie 生成时机与触发流程观察下来这个 cookie 的生成流程大致是这样的无痕浏览器首次访问东航首页。服务端返回 HTMLHTML 中引用了一个加密 JS 文件。浏览器加载该 JS 并执行脚本内部采集时间戳、浏览器特性、运行环境参数。脚本执行到某个节点时调用document.cookie写入ssxmod_itna。页面继续加载时脚本再次执行生成第二个值ssxmod_itna2。这两个 cookie 的生成有先后顺序不是一次性写入的。ssxmod_itna通常在页面加载早期生成ssxmod_itna2会在稍后的异步流程中出现。这个顺序很重要因为在还原算法时不能只盯着某一个字符串要理解整段脚本的生命周期。这就好比进一个小区需要先刷门禁卡进大门再刷单元门禁。两个 cookie 就是两道门禁的密码密码本身都在同一个“门禁系统”里生成但生成时机不同。3.2 加密算法的典型特征很多做算法分析的人会预期“解一个哈希”或者“解一个 AES 加密”但真实情况往往不是那么直接。这类动态 cookie 的算法通常有以下几个典型特征时间参与计算cookie 的内容里会编码一个时间戳。服务端拿到 cookie 后会对比自己收到请求的时间和 cookie 里记录的时间差超过一定阈值就直接拒绝。浏览器指纹参与计算User-Agent、Canvas 指纹、WebGL 渲染信息、屏幕分辨率、语言设置等都可能被采集并拼接到加密原文中。状态机设计脚本并不是单纯算一个固定公式而是维护一个内部状态数组。数组中的值按某种规则不断变化最终序列化后写入 cookie。这就导致每次生成的值都不一样很难通过“比对固定输入输出”来反推算法。混淆程度高变量名是一堆无意义字符字符串可能被拆成多个片段后用concat拼接甚至直接用atob解码。直接静态阅读基本不可能。这些特征加起来给我们的启示是不要试图用“找到加密公式”的思维去理解它而要用“追踪数据流”的思维。核心问题不是“它用了什么算法”而是“哪些数据参与了计算计算结果的格式是什么”。3.3 定位核心代码的实操方法定位核心代码是整个分析过程中最关键的一步我整理了一套可以复用的操作流程。首先打开目标页面在 DevTools 的 Application 面板中查看 Cookies确认ssxmod_itna已经出现。接着切到 Sources 面板用快捷键 CtrlShiftF 全局搜索ssxmod_itna这一步可以直接找到写入 cookie 的那行代码。找到后在这一行打上断点然后刷新页面。脚本执行到这一行时会自动暂停这时候在右侧的 Call Stack 中就能看到完整的调用链往前追溯几步就是生成该值的核心计算函数。如果断点打不上或者位置不准确还有一个更直接的插桩方法在 DevTools 的 Console 面板中提前执行一段代码重写document.cookie的 setter把 cookie 写入动作拦下来。(() { const originalSet Object.getOwnPropertyDescriptor(Document.prototype, cookie).set; Object.defineProperty(document, cookie, { get() { return Object.getOwnPropertyDescriptor(Document.prototype, cookie).get.call(this); }, set(value) { console.log([Cookie Setter], value); debugger; originalSet.call(this, value); } }); })();这段代码的作用是在页面脚本写入 cookie 时自动打印对应的值并触发debugger让执行暂停。这样就算你不确定具体写入代码在哪一行也能借助调用栈快速摸到生成逻辑。提示这种插桩方法对几乎所有同类动态 cookie 都有效。原理是前端脚本无论怎么写 cookie最终都必须调用浏览器原生的document.cookiesetter我们只要在这个公共入口处“装一个窃听器”就能抓到所有线索。4. 完整实操过程从抓包到本地复现4.1 第一步确认入口与捕获流量我先用 Fiddler 开启了 HTTPS 解密然后打开无痕浏览器访问东航首页。在 Fiddler 的会话列表中按域名过滤只关注东航主域名下的请求。重点看第一个 HTML 响应和紧随其后的 JS 文件请求。比较有意思的是这个 JS 文件的文件名每次访问都会变化且响应内容长度在几十 KB 左右。这个量级的 JS 经过混淆后往往包含不止一个功能还可能包含后续页面交互时需要的事件监听逻辑。通过查看响应内容确认ssxmod_itna字符串出现在这段 JS 中。此时我基本确定cookie 的生成逻辑完全在前端服务端只负责校验不参与计算。这对后续还原是非常有利的因为省去了寻找暗桩和模拟加密请求的步骤。4.2 第二步插桩与调用栈分析用上面的插桩代码在 Console 里执行后刷新页面。控制台立刻输出了ssxmod_itna的写入记录同时代码暂停在 debugger 位置。我这时候不看当前行而是直接看右侧 Call Stack。调用栈的核心是找到两层信息哪个函数真正调用了document.cookie。这个函数的参数列表是什么参数从哪里来。记录下关键函数名后我点击调用栈里的对应位置跳转到了加密脚本源码中。源码是经过变量名混淆的所有函数名和变量名都是类似_0xabc123的形式。我没有立刻尝试还原整个文件而是优先定位“这个函数的上级调用者是谁”通过上级调用者进一步确定输入参数。这一步的收获是ssxmod_itna的结果中确实包含一个可读的 base64 片段。用 base64 解码后发现里面是一个 JSON 对象包含时间戳和若干环境参数。这说明算法至少有三层第一层采集环境参数。第二层将参数序列化为 JSON。第三层对 JSON 做某种变换后输出。4.3 第三步算法还原与本地验证算法还原是最耗时的一步我的思路是“从结果倒推”。既然知道输出是一个 base64 片段就先解码看内部结构既然知道里面有 JSON就进一步观察 JSON 的哪些字段是动态变化的哪些是固定的。以我这次的案例来看JSON 中有一个时间戳字段和多组环境指纹字段。时间戳是标准的毫秒级 Unix 时间戳环境指纹里有一部分来自navigator对象。服务端校验时大概率会检查时间戳差值并比对环境指纹是否和自己记录的客户端特征一致。后续完整还原就不说得太细了因为不同站点的实现差异极大。但我可以分享一个经验如果只是想让脚本“能跑通”完全不必把整个算法用 Python 重写一遍。更实用的方案是用 Playwright 启动一个真实浏览器让它加载页面并执行原始 JS然后从浏览器上下文中直接读取生成完毕的 cookie再交给 requests 使用。import asyncio import requests from playwright.async_api import async_playwright async def get_dynamic_cookies(url): async with async_playwright() as pw: browser await pw.chromium.launch(headlessFalse) context await browser.new_context( user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... ) page await context.new_page() await page.goto(url, wait_untilnetworkidle) cookies await context.cookies() await browser.close() return {c[name]: c[value] for c in cookies} async def main(): target https://www.ceair.com/ dyn_cookies await get_dynamic_cookies(target) print(dyn_cookies) session requests.Session() session.cookies.update(dyn_cookies) resp session.get(https://www.ceair.com/, headers{ User-Agent: Mozilla/5.0 ... }) print(resp.status_code) asyncio.run(main())这段代码的思路是把“生成 cookie”这个任务交给浏览器把“发送请求”这个任务交给 requests两者通过 cookie 共享协作。每次执行任务前先启动浏览器生成新鲜的 cookie再用它发起请求这样既绕过了复杂的算法还原又保证了 cookie 的有效性。注意这里headlessFalse是有意为之的。部分风控逻辑会检测“无头浏览器”特征即便算法完全正确一旦检测到 headless 环境也会拒绝。实际使用时可以先尝试 headless如果被拦就改成有头模式或者对 headless 特征做进一步规避。4.4 第四步纯本地重写的判断标准用 Playwright 方案虽然稳定但缺点是每运行一次都要启动一个浏览器速度慢、资源占用高。如果对速度有要求比如每秒要多线程发起请求那就必须走“纯本地重写”的路线。判断是否值得重写主要看三点算法是否依赖浏览器特有对象。如果大量使用document、navigator、canvas纯本地重写就需要模拟这些对象成本很高。脚本是否内置了环境检测的“暗桩”。有些脚本在检测到非浏览器环境时不会直接报错而是静默生成一个错误的 cookie。这种暗桩排查起来非常折磨人。是否包含动态代码执行。如果脚本里有大段的new Function、eval那么静态重写几乎不可能只能也必须保留 JS 本身执行环境。在我这个案例里由于时间有限我最终选择了“保留 JS 执行环境”的方案也就是在 Node.js 里通过vm模块直接执行原始加密脚本并在执行前通过vm上下文预置所需的浏览器对象。const vm require(vm); const sandbox { navigator: { userAgent: Mozilla/5.0 ..., language: zh-CN, platform: Win32 }, screen: { width: 1920, height: 1080 }, document: { cookie: , getElementById: () null, querySelector: () null }, localStorage: { getItem: () null, setItem: () {} }, setTimeout, console }; sandbox.window sandbox; const jsCode require(fs).readFileSync(encrypted.js, utf-8); vm.createContext(sandbox); vm.runInContext(jsCode, sandbox, { timeout: 3000 }); console.log(sandbox.document.cookie);这段代码只是框架示例实际运行时还得根据目标脚本的具体引用不断往 sandbox 里补环境对象。跑通一次后后续只要把采集到的真实指纹参数填进去就能在本地稳定生成 cookie。5. 常见问题与排查技巧实录5.1 获取到 cookie 但请求还是 403这是一开始最容易遇到的坑。获取到 cookie 并不代表一切正常服务端校验时还会看多个维度的信息。常见的失败原因有三个时间戳过期从获取 cookie 到发起请求之间的等待时间太长。服务端对时间差有容忍阈值超过就拒绝。解决方法是把获取 cookie 和发起请求两件事放在同一个流程里中间不要插入耗时操作。UA 不匹配生成 cookie 时浏览器环境里的 User-Agent 和后续请求头里的 User-Agent 不一致。服务端会比对 cookie 中编码的环境参数和请求头中的实际环境不一致直接拒绝。缺少其他配套 cookie动态安全产品有时不只生成两个 cookie可能还会在后续交互中更新其中之一。只带旧 cookie 不带新 cookie同样会被拒。排查方法是用浏览器正常访问在 Network 面板找到一条成功请求复制这条请求的完整 Header 和全部 Cookie把它做成一个完整的“请求模板”再逐步删减非必要字段测试出服务端到底校验了什么。5.2 算法还原出来但本地生成的值被拒这种情况往往说明环境参数没有完全模拟。比对本地的 navigator 参数和浏览器实际值经常能发现差异点参数浏览器真实值本地模拟值navigator.userAgent具体浏览器 UA缺省或过旧版本screen.width19200 或未定义navigator.plugins插件列表空数组canvas.toDataURL一段非空 base64undefinedCanvas 这类指纹参数很难凭空编造因为它和显卡驱动、字体渲染都有关系。遇到这种情况最有效的方法还是回到浏览器环境中执行 JS不要硬在本地伪造。5.3 混淆代码完全读不懂怎么办不要硬读。先用 de4js 自动还原一遍很多时候可以直接把变量名替换成可读名字。如果还原后还是有大量嵌套逻辑就放弃全文阅读改用“单步追踪”的方式只关心几个数据流节点环境参数在哪里被采集。采集结果存储到哪个变量。这个变量经过哪些函数调用变成新值。新值最终如何被拼接成 cookie 字符串。用这个思路可以绕过 90% 的无关代码只关注一条完整的数据生命周期。其他一切统统忽略。5.4 动态 cookie 能否静态复用有读者可能会想我拿到一组有效 cookie能不能一直用下去实测结果是能复用一段时间但非常不稳定。这组 cookie 是和时间绑定的时间窗口过了就必须刷新。有些自动化脚本会在多次请求之间维护一个 cookie 池周期性地用浏览器补采新 cookie再配合 requests 使用。这种混合架构在实践中比“每次请求都启动浏览器”快得多也比“纯本地重写”稳得多是一种值得参考的折中方案。6. 个人经验与合规提醒6.1 踩过几次坑之后的体会做这类分析最大的教训是“不要一开始就挑战最难的路线”。我最初花了整整两天尝试纯本地重写算法不停地在 JavaScript 混淆代码里打转进度很慢。后来换了个思路先用 Playwright 把整条流程跑通确认“动态 cookie 方案可行”再去研究算法细节反而更快地定位到了核心计算逻辑。另一个心得是保存 cookie 时一定要连带当时的 User-Agent、Accept-Language 等 Header 一起保存。很多“明明有 cookie 却被拒绝”的案例问题都不在 cookie 本身而在于其他环境字段没有配套保持一致。把“环境快照”这个概念记在心里能少踩很多坑。6.2 场景延伸的思考这个分析思路并不仅限于民航类网站。任何依赖动态 JS 生成 cookie 的前端风控系统都可以用同一套方法论来研究先抓包确认入口再插桩定位写入点然后追踪数据流还原算法最后评估是用浏览器执行还是本地重写。做这类工作时始终要记得确认自己是否有权限和技术目的合规性。如果你的目标是接口自动化测试那么申请官方对接渠道永远是优先选项算法分析更多是在没有现成接口、且业务上确有技术研究需要时才考虑的手段。我在实际使用中发现把“浏览器生成 cookie requests 发请求”这套组合维护好已经能覆盖绝大多数自动化场景。至于把算法彻底还原成纯 Python只有在性能要求极高时才值得投入。先跑通再优化永远是这类技术活最务实的推进方式。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。