不安全的跨域资源共享(CORS)中的“预检请求缓存中毒”(Preflight Cache Poisoning)漏洞与防护
字数 5287
更新时间 2025-12-28 15:10:05

不安全的跨域资源共享(CORS)中的“预检请求缓存中毒”(Preflight Cache Poisoning)漏洞与防护


题目描述

这是一个关于跨域资源共享(CORS)的高阶攻击与防护题目。我们探讨的场景是:攻击者可能通过滥用CORS预检请求(Preflight Request)的缓存机制,来实施缓存投毒攻击,从而破坏同源策略的保护,导致跨域攻击的权限提升。

简单来说,CORS预检请求是浏览器在发送某些“非简单跨域请求”(如使用了Content-Type: application/json或自定义头部的请求)前,自动发送的一个OPTIONS请求,用于获取目标服务器是否允许该跨域请求。服务器和中介(如CDN、反向代理、网关)可能缓存这个OPTIONS请求的响应。如果攻击者能够操纵或污染这个缓存,就可能使得后续合法的跨域请求绕过安全检查,或者将恶意数据注入到缓存中,影响其他用户。

核心问题: 不安全的CORS配置(特别是Access-Control-Allow-*相关头部的设置)与不安全的中间件缓存策略结合,导致攻击者可以利用缓存的OPTIONS响应,为后续请求授予本不应有的跨域权限,实现如反射型CORS攻击权限提升数据窃取


解题过程与细致讲解

我们将分四个步骤,循序渐进地理解这个漏洞的原理、利用条件和防御方法。

第一步:理解基础——CORS预检请求(Preflight Request)与缓存

  1. 为什么需要预检请求?
    • 浏览器同源策略(SOP)默认阻止跨域读取某些资源。CORS是一种机制,允许服务器声明哪些“外源”(不同协议、域名、端口)可以访问其资源。
    • 对于“非简单请求”(如使用PUTDELETE方法,或Content-Type: application/json,或携带自定义头如X-Custom-Header),浏览器会先发送一个OPTIONS方法的“预检请求”到目标服务器。
    • 这个OPTIONS请求会携带几个特殊头部,例如:
      • Origin: 发起请求的源(如https://evil.com)。
      • Access-Control-Request-Method: 实际请求打算使用的方法(如POST)。
      • Access-Control-Request-Headers: 实际请求打算携带的自定义头部列表(如X-Custom-Header)。
  2. 服务器的响应与缓存
    • 服务器应针对OPTIONS请求返回响应,包含关键的CORS相关头部,例如:
      • Access-Control-Allow-Origin: https://trusted.com (或*,但不安全)。
      • Access-Control-Allow-Methods: POST, GET, OPTIONS
      • Access-Control-Allow-Headers: X-Custom-Header, Content-Type
      • Access-Control-Max-Age: 86400 (单位秒,指示浏览器可以缓存此预检响应多长时间,在此期间内对同一URL的相同跨域请求不再发送预检)。
    • 关键点: 这个OPTIONS请求的响应不仅可能被浏览器缓存(根据Access-Control-Max-Age),还可能被服务器与客户端之间的任何中间件(如CDN、反向代理、API网关)缓存,特别是当这些中间件将OPTIONS方法视为可缓存的GET请求的变体时,或者缓存配置不区分HTTP方法时。

第二步:漏洞原理——攻击者如何“下毒”?

攻击者的目标是污染这个缓存的OPTIONS响应,使其包含有利于攻击的CORS策略。

攻击场景示例

  1. 前提条件
    • 目标服务器(api.victim.com)配置了宽松或不安全的CORS策略,例如Access-Control-Allow-Origin: * 或动态反射请求中的Origin头(即Access-Control-Allow-Origin: [请求中的Origin值])。
    • api.victim.com前面有一个CDN或反向代理,并且它缓存OPTIONS请求的响应。其缓存键(Cache Key)可能设计不当,例如仅基于请求URL,而忽略了Origin
  2. 攻击步骤
    • 步骤1(投毒): 攻击者从一个受其控制的恶意源(https://evil.com)向api.victim.com发送一个精心构造的OPTIONS预检请求。
      • 请求头包含:Origin: https://evil.comAccess-Control-Request-Method: POST
    • 步骤2(服务器响应与缓存): 由于服务器配置了反射Origin的策略,它会返回响应头:Access-Control-Allow-Origin: https://evil.com。这个响应被中间的CDN缓存了下来。由于CDN的缓存键只用了URL(如https://api.victim.com/api/data),没有包含Origin头,所以这个缓存的条目与Origin值无关。
    • 步骤3(缓存命中与污染生效): 随后,一个来自合法、受信任源(https://trusted-app.com)的用户(受害者)访问https://trusted-app.com,该页面尝试向https://api.victim.com/api/data发送一个跨域POST请求。浏览器触发预检,发送OPTIONS请求,其Origin头为https://trusted-app.com
    • 步骤4(权限错误授予): 这个OPTIONS请求到达CDN。CDN发现其URL(https://api.victim.com/api/data)在缓存中存在一个条目(即步骤2中攻击者污染的条目),于是直接返回缓存的响应,而没有将请求转发给源服务器。这个缓存的响应中,Access-Control-Allow-Origin的值是https://evil.com,而不是https://trusted-app.com
    • 步骤5(攻击影响): 浏览器收到这个缓存的响应,发现Access-Control-Allow-Originhttps://evil.com)与当前请求的源(https://trusted-app.com)不匹配。根据CORS规范,浏览器应该拒绝此跨域请求。然而,在某些早期的浏览器实现或特定配置下,如果服务器响应了Access-Control-Allow-Credentials: true(允许携带凭据如Cookie),且Origin为*以外的特定值,浏览器可能会错误处理。更常见的利用是,如果服务器配置为反射Origin且不验证,攻击者可以诱导用户从trusted-app.com发起请求,但由于缓存投毒,服务器响应的CORS头允许了evil.com,这可能导致后续的实际请求(如包含敏感操作的POST)被发送,且响应可能被evil.com的页面通过XMLHttpRequest读取(如果配置不当),造成敏感数据泄露。另一种影响是,污染的CORS头可能包含过长的、恶意的头部值,用于进行客户端攻击(如头注入)。

简单比喻: 就像一个酒店的安检口(CORS检查)。正常情况下,客人(浏览器)需要出示身份证(Origin)并登记。酒店前台(服务器)会根据名单核对。现在,攻击者提前用假身份证(evil.com)登记了一次,并且这个登记记录被粗心的保安(CDN缓存)记在了通用登记本上(只记录房间号/api/data,不记录身份证号)。当真正的客人(trusted-app.com)来登记时,保安直接翻出通用登记本,把之前假身份证的信息给了他,导致真正的客人要么被拒绝进入,要么被赋予了错误的权限。

第三步:漏洞利用的深入与变种

  1. 反射型CORS攻击的结合: 如果服务器直接将请求中的Origin头值不加校验地反射到Access-Control-Allow-Origin响应头中,攻击者可以在预检请求中注入一个恶意源(如evil.com)。当这个响应被缓存后,任何后续来自其他源的预检请求都可能收到这个允许evil.com的响应,从而为evil.com打开了一个时间窗口,使其能发起跨域请求并读取响应(如果服务器还配置了Access-Control-Allow-Credentials: true,且客户端代码使用withCredentials,危险更大)。
  2. 缓存键操纵: 攻击的核心在于中间件的缓存键设计。如果缓存键不包括Origin头,或者对OPTIONS方法的处理逻辑与GET相同,就容易受到攻击。攻击者可能会尝试污染不同路径、不同查询参数的缓存条目。
  3. Vary头的缺失: 一个正确的、防御此类攻击的缓存机制,对于CORS响应,应该在响应头中包含Vary: Origin。这个头部指示缓存系统:当请求头中的Origin值不同时,应该被视为不同的资源,不能使用同一个缓存副本。如果服务器或应用在返回OPTIONS响应时没有正确设置Vary: Origin,就为缓存投毒创造了条件。

第四步:防护与缓解策略

防护需要从服务器/应用配置基础设施/中间件配置两个层面入手。

  1. 严格的CORS策略配置(应用层)

    • 避免使用通配符*: 尽量不要设置Access-Control-Allow-Origin: *,尤其是当请求需要凭证(Cookies、Authorization头等)时。应明确指定允许的源列表,并在服务器端进行校验。
    • 严格校验Origin: 对于反射Origin的策略,必须在服务器端有一个严格的白名单,只反射那些在白名单中的Origin值。绝对不要信任客户端传来的任何Origin值。
    • 避免过度宽松的Access-Control-Allow-MethodsAccess-Control-Allow-Headers: 只列出应用实际需要的方法和头部,而不是*
  2. 正确设置缓存控制头(应用层与中间件层)

    • 强制Vary: Origin头部: 确保所有CORS响应(包括对OPTIONS预检请求和实际请求的响应)都包含Vary: Origin响应头。这是防御预检请求缓存投毒的最关键措施。它明确告知缓存系统,Origin请求头的值是决定缓存版本的关键因素。
    • 谨慎使用Access-Control-Max-Age: 虽然设置较长的Access-Control-Max-Age可以提高性能,但也会延长潜在污染缓存的影响时间。在安全要求高的场景,可以适当减少此值,或仅在源(Origin)高度可信、变化不频繁的场景下使用较大值。
  3. 安全的中间件/基础设施配置(运维/架构层)

    • OPTIONS请求配置独立的缓存策略: 在CDN、反向代理等中间件上,明确配置不对OPTIONS方法的请求进行缓存,或者为OPTIONS请求设置非常短(如0秒)的缓存时间。
    • 确保缓存键包含Origin: 如果必须缓存OPTIONS响应,必须确保中间件的缓存键(Cache Key)将Origin请求头作为计算因子之一。这样,不同源的OPTIONS请求就不会共享同一个缓存条目。这是实现Vary: Origin语义的基础设施保障。
    • 定期审计与测试: 使用自动化工具或手动测试,验证CORS配置的安全性,特别是模拟攻击者从不同源发起请求,检查响应头(Access-Control-Allow-OriginVary)和缓存行为是否符合预期。
  4. 深度防御

    • 实施同源策略作为基础: 理解CORS是对SOP的有限、可控放宽。默认情况下,不提供跨域访问是最安全的。
    • 结合其他安全机制: 即使CORS配置得当,也需要其他安全措施,如输入验证输出编码使用CSRF令牌等,防止因CORS配置失误或被绕过而导致严重安全问题。

总结: “CORS预检请求缓存中毒”漏洞是一个典型的安全机制交互缺陷案例。它提醒我们,单个安全机制(CORS)配置不当,与另一个性能优化机制(缓存)配置不当相结合,会产生新的攻击面。防御的关键在于理解整个请求-响应链中每个环节的行为,并通过正确设置HTTP头部(特别是Vary: Origin)和中间件策略,确保安全策略在缓存层得到正确传递和执行。

相似文章
相似文章
 全屏