携程的防护结构：标识体系比签名复杂逆向携程接口时容易陷入一个误区：把 sign 参数当主角。实际上携程的防护重心在设备标识体系——GUID、clientid、UBT 埋点标识、指纹 fp 互相咬合，sign 只是把这些标识串起来的最后一环。先理清标识体系，签名水到渠成。标识体系拆解GUID：32 位标识，首次访问由服务端 Set-Cookie 种下，长期有效。它是携程体系里的"设备身份证"clientid：请求头里的客户端标识，和 GUID 关联生成UBT 系列：埋点 cookie（_UBT、_bfa 等），记录页面行为路径，部分接口会校验其存在性fp：指纹值，独立 SDK 采集生成，和 ua/设备参数绑定协议模拟的铁律：这些标识成套生成、成套固定。拿 A 设备的 GUID 配 B 设备的 fp，服务端一致性校验直接命中"设备环境异常"。sign 的定位与结构携程 H5 的 sign 由 hybrid 框架的 bridge 层生成，定位入口：// 携程 H5 大量走 hybrid bridge，hook 点选请求封装层 const _open = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(m, u) { if (u.includes('sign=') || u.includes('/restapi/')) debugger; return _open.apply(this, arguments); };断下后回溯到 bridge.js 的签名模块，结构是标准的：收集 url path、body、时间戳、clientid、GUID竖线分隔拼接待签串HMAC-SHA1（特征常量 0x67452301，在 JS 里搜十进制形式定位）后 hex 大写按版本特定的规则截取/变换后输出密钥分端存储：H5、小程序、App 各一把。H5 的在字符串池里，运行时解密——老规矩，在使用点下断点拿明文，不静态分析解密过程。请求编排：顺序比参数重要携程接口对"访问路径"有隐性校验。直接打业务接口容易返回空数据，正确的编排是模拟真实用户路径：session = requests.Session() # 1. 先进站，拿 GUID 等基础 cookie session.get('https://m.ctrip.com/') # 2. 访问业务频道页，种下频道相关 cookie 和 UBT session.get('https://m.ctrip.com/html5/flight/') # 3. 带齐标识打业务接口 resp = session.post('https://m.ctrip.com/restapi/...', headers={'clientid': cid, 'sign': sign, ...}, json=body)跳过前两步直接第三步，是"参数全对但返回空"的最常见原因。精髓：四个实战要点签名失败无报错。携程对 sign 错误的响应是返回空数据或通用错误页，不明确提示。调试基准：用"查询城市列表"这类公开接口验证签名链路，通了再接业务接口。GUID 的"养"。新 GUID 直接打敏感接口容易触发验证，先在浏览类接口上"养"一段时间（积累正常访问记录）再上业务，通过率显著提升。App 端是 native 签名。携程 App 的 sign 在 SO 层（Ctrip 安全库），和 H5 不通用。App 协议走 Frida hook 签名函数的路线，思路同得物/闲鱼。国际版（Trip.com）是独立体系。域名、appid、密钥都不同，但算法结构一致——搞定国内版后迁移成本很低。总结携程防护重心在标识体系：GUID、clientid、fp 成套管理是地基sign 是 HMAC-SHA1 体系，密钥在使用点内存拿请求编排模拟真实路径，裸打业务接口必翻车调试用公开接口做基准，签名错误无明确报错