首页 / 资讯动态 / 移动端H5页面防CC恶意访问防护技巧

移动端H5页面防CC恶意访问防护技巧

移动端H5页面面临的CC攻击,本质上不是比谁带宽大,而是比谁更懂HTTP协议的无状态特性。攻击者利用H5页面必须暴露给公网的API接口,用极低的成本伪造大量正常请求,耗尽服务器连接池或触发数据库的慢查询。防护的核心思路不是“拦截恶意请求”,而是“验证请求背后是否有一个真实的、完整的浏览器环境”。

把TLS握手变成第一道筛选器

多数CC攻击工具为了追求并发量,会省略或简化TLS握手过程。在CDN或反向代理层开启TLS 1.3的0-RTT早期数据功能时,要同时强制验证客户端的JA3指纹。JA3是TLS客户端Hello包的MD5哈希值,正常移动端浏览器如iOS Safari、Android Chrome的JA3指纹是固定且已知的。在Nginx上集成nginx-module-ja3模块后,可以在SSL握手阶段直接拒绝指纹不在白名单内的请求,根本不消耗后端资源。对于必须兼容的低版本WebView,可以维护一个动态指纹库,通过定期采集真实用户的实际指纹来更新白名单。这种做法把防御线推到了TCP连接建立之后、HTTP请求到达之前,CC工具的连接池会在几秒内被大量RST包填满。

利用服务端挑战验证浏览器完整性

当请求通过TLS层后,不要立即透传给业务后端。在边缘节点注入一段极简的JavaScript挑战代码,这段代码不依赖任何第三方库,只做三件事:检测navigator.webdriver是否为false、执行一个非对称的数学运算如生成指定难度的哈希原像、读取屏幕的colorDepth和硬件并发数。挑战通过后,由前端将结果通过自定义请求头回传,边缘节点验证后签发一个短时效的加密Token。这个Token要绑定客户端的ASN号和/24网段,防止验证通过后Token被转移到其他IP使用。对于纯API调用的场景,无法执行JS,这时可以返回HTTP 428状态码,要求客户端携带正确签名的验证头,合规的客户端会按照约定算法生成签名,而简单的脚本无法自动处理这种交互。

全链路的请求头行为分析

移动端H5页面发出的HTTP请求,其请求头顺序、大小写组合、Accept-Language的精细度、Viewport-Width的数值特征,在不同系统和WebView下呈现出高度一致的规律。例如iOS 17上的Safari,其Accept头中image/avif和image/webp的出现顺序是固定的,Sec-CH-UA-Mobile的值一定是?1,且Sec-CH-UA-Platform必然是iOS。攻击脚本通常使用curl或python-requests库,它们的默认请求头顺序是字母序排列,缺少Sec-CH-UA这类客户端提示头,或者Accept-Language只设置成en-US。在网关层建立一套规则引擎,对每个请求的头部组合进行评分,缺少移动端特征头的扣分,头部顺序异常的扣分,声称是iOS但Sec-CH-UA-Platform是Windows的直接拒绝。这套规则不需要机器学习,用几百行Lua脚本在OpenResty里就能实现,延迟增加不到1毫秒。

无感的人机验证与行为序列

传统的滑块验证码在移动端体验极差,但可以将验证逻辑隐藏在正常的用户交互中。当用户点击按钮、滚动页面或输入内容时,采集触摸事件的pressure、radiusX、radiusY属性,以及从touchstart到touchend的时间间隔。人类手指在玻璃屏幕上的触摸面积会随按压力度产生微小变化,时间间隔通常在80到200毫秒之间,而脚本模拟的点击事件这些属性值全是零或固定常数。将这些生物特征数据哈希后作为请求的校验因子,在后端进行累计评分。如果一个IP在短时间内发起大量请求,但每次请求携带的触摸特征哈希都相同,或者根本没有携带这个字段,就直接判定为自动化工具。

接口级别的资源消耗控制

CC攻击最有效的手段是找到那些会触发高计算量的接口,比如列表翻页到第1000页、模糊搜索一个空字符串、或者请求生成一张复杂的动态海报。防护上要做接口级别的资源预算管理。对于列表类接口,强制要求客户端传递一个由服务端签名的游标,这个游标里加密了当前页码和会话ID,任何试图跳过页码或修改游标的行为都会导致签名校验失败。对于搜索接口,设置最小输入长度阈值,且对单个搜索词的结果集大小做硬性限制。最关键的是,在应用服务器内部,为每个会话分配一个计算时间片,如果一个会话在1秒内消耗的CPU时间超过50毫秒,就将其后续请求降级处理或直接排队。这种控制要放在业务逻辑层的最外层,用装饰器模式包裹每一个Controller,确保任何路径都无法绕过。

利用Service Worker构建客户端防火墙

如果H5页面已经升级到HTTPS并注册了Service Worker,就拥有了一层极其强大的客户端防御能力。在Service Worker的fetch事件监听器中,可以拦截页面发出的所有网络请求,检查请求URL是否匹配已知的攻击模式,例如对同一接口的重复请求、对不存在的资源路径的探测。一旦检测到异常频率,Service Worker可以直接返回一个自定义的Response对象,阻止请求真正发出,同时在客户端本地记录异常次数。当异常次数超过阈值,Service Worker可以主动注销自己,让页面退化为普通模式,并向服务端上报该设备的标识符。这种客户端侧的拦截不消耗任何服务端资源,而且攻击者很难感知到Service Worker内部逻辑的变化,因为修改Worker脚本后,浏览器会在后台静默更新。

动态的API路径和参数混淆

对于不需要被搜索引擎收录的H5业务接口,可以采用动态路径策略。在用户首次访问时,服务端下发一个会话级别的路径映射表,例如/api/user/info的真实访问路径被映射为/api/v3/8a7d9f,这个随机后缀每半小时轮换一次。前端在发请求前,根据映射表动态组装URL。攻击者在编写脚本时,通常硬编码了从抓包工具中看到的固定路径,当路径失效后,他们的请求会全部落在404上,而404页面的处理成本远低于业务接口。同时,在请求体中增加一个由时间戳和会话ID联合生成的动态参数,服务端校验这个参数的时间窗口和签名,即使攻击者截获了完整请求,超过30秒后该请求也无法重放。

基于连接行为的状态机检测

在四层负载均衡器或网关层面,为每个源IP维护一个简单的TCP连接状态机。正常移动端用户在访问H5页面时,会先建立连接请求HTML文档,解析后再并行建立几个连接请求CSS、JS和图片资源,之后连接会保持一段时间用于API交互。而CC攻击工具的连接模式通常是:建立连接、发送一个请求、收到响应后立即关闭连接,或者用极快的速度在同一个连接上连续发送请求。通过跟踪每个IP的连接建立速率、请求间隔的熵值、以及连接复用的比例,可以精准区分人类和脚本。当某个IP的连接模式偏离正常范围时,将其加入慢速通道,人为增加响应延迟,打乱攻击工具的并发节奏。

利用缓存穿透保护源站数据库

CC攻击经常通过构造随机查询参数来穿透CDN缓存,直击源站数据库。防护上不能只依赖CDN厂商的默认规则。在源站前部署一层Varnish或Nginx缓存,配置为只要URL中包含特定的查询参数模式,就强制缓存一个短时间的空响应。例如对于搜索接口,如果查询参数q的值是随机字符串且长度超过20个字符,直接返回空结果并缓存10秒。对于需要实时性的接口,使用代理缓存的热数据保护机制:当后端数据库的查询延迟超过预设阈值时,自动切换到返回缓存中的最后已知良好数据,而不是让请求堆积在数据库连接池上。这种降级策略保证了即使攻击穿透了前面所有防线,核心数据库也不会被拖垮。

多维度聚合限流与自动解封

单一维度的IP限流在移动端场景下很容易误伤,因为运营商NAT会导致大量用户共享同一个出口IP。需要建立多维度的聚合限流体系,维度包括:源IP、会话Cookie、设备指纹、请求的API端点、以及用户ID。每个维度独立计数,任何一个维度超过阈值就触发该维度的限制。例如某个IP整体请求量高,但该IP下每个会话的请求量都正常,说明是NAT环境下的正常用户聚集,此时只对IP进行宽松限制,不影响已登录用户。反之,如果某个会话在短时间内请求了多个不同用户的资料接口,这是典型的横向越权探测,应立即封禁该会话。封禁操作要分级执行,第一级是增加验证挑战,第二级是限速到每秒1个请求,第三级才是完全拒绝,且所有封禁都设置自动过期时间,避免永久性误封。

实时监控与攻击特征回溯

防护规则不是一成不变的。在每次遭受攻击后,需要有一套机制能快速回溯攻击流量,提取新的特征。将网关的访问日志实时写入ClickHouse或Elasticsearch,保留请求头、响应状态码、响应时间、以及经过哈希处理的客户端标识。当监控告警触发时,通过SQL查询找出攻击流量与正常流量在User-Agent字符串细微差异、请求路径的聚类特征、甚至TCP窗口大小的分布差异。这些新发现的特征可以迅速转化为下一版防护规则,形成持续进化的防御体系。攻击者的工具在迭代,防御者的规则库也必须以同样的速度更新,而数据就是规则更新的唯一可靠来源。