1. CORS跨域访问漏洞到底是什么能做什么很多人在调接口时都见过这样的报错has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource。这一行英文前端不认识、后端不承认、运维一头雾水最后要么是后端粗暴地加上Access-Control-Allow-Origin: *要么是从网上复制一段中间件配置完事。但在我做Web安全测试和SRC漏洞挖掘的这几年里最常翻车、也最容易被低估的恰恰就是CORS。很多开发人员以为CORS只是“跨域时的一个头”配错了顶多导致页面拿不到数据实际上CORS跨域配置错误是一种实打实的漏洞严重情况下可以直接用受害者身份读取他个人数据、银行卡信息、通讯录甚至后台订单数据属于典型的逻辑层配置风险也是我在漏洞报告里经常能拿高危评级的突破口。我写这篇文章主要想给你讲透三件事CORS跨域漏洞是怎么形成的怎么在真实项目里验证它以及一旦确认存在该怎么修。适合这几类人看正在学SRC漏洞挖掘和渗透测试的初学者被CORS报错折磨的前端或后端开发以及做安全巡检或漏洞扫描的运维同学。我会把原理、利用步骤、排查过程和修复方案全部串起来大部分内容都可以直接在你的测试环境里照着做一遍。2. CORS工作原理与漏洞成因拆解2.1 从同源策略开始说起浏览器安全模型中最核心的一条规则叫同源策略。所谓“同源”指的是协议、域名、端口三者完全一致。比如https://a.com:443/page和https://a.com:443/api是同源但https://a.com和http://a.com不同源https://a.com和https://b.com也不同源https://a.com:8080和https://a.com还是不同源。同源策略限制了不同源之间的资源读取一个页面里的JavaScript只能读取同源接口的数据否则就会被浏览器拦截。这套机制就好比你在自己家里可以随便开抽屉但是到了邻居家对方不开门你就什么都拿不到。但真实业务不可能永远同源。前端页面部署在www.example.com后端接口却在api.example.com或者前端服务器和后端服务器域名都不一样这时候必须有一种机制允许特定跨域请求通过。于是W3C制定了跨域资源共享标准也就是CORS。CORS的核心逻辑是浏览器发现一个跨域请求后会先把请求的Origin头告诉服务器服务器根据这个Origin判断是否允许、允许哪个来源再通过响应头Access-Control-Allow-Origin告诉浏览器。浏览器只认这个响应头如果响应里没有它或者它的值不等于请求的Origin就会拦截响应页面里的JS就拿不到数据。这个流程设计得很合理问题出在很多人配置CORS时图省事、图简单把校验逻辑写成了“不管谁来都放行”或者“谁带哪个Origin就放行谁”这就等于邻居家门没锁任何人都能进去翻东西。2.2 三类典型的CORS配置错误我梳理了实战中遇到最多的三种错误的CORS配置很多网上公开的CORS跨域漏洞报告根源都在这三类里。第一类是直接配置Access-Control-Allow-Origin: *。这个通配符的意思是允许任意来源跨域读取。如果接口涉及用户隐私数据、个人订单、账户信息等配合任意来源这一点任何网站都可以在用户访问时静默发起跨域请求并读取结果。不过有一个细节如果响应里同时带上了Access-Control-Allow-Credentials: true浏览器会直接拒绝这种组合因为标准规定通配符不能配合凭据使用。于是很多人在踩了这个坑之后把通配符改成了反射Origin这就进入了第二类。第二类是反射任意Origin也就是服务器不做任何校验直接把请求头里的Origin原封不动地写进响应头Access-Control-Allow-Origin。这种配置最常见我在不少企业内部系统、政府网站、电商后台里都见过。攻击者只需要把恶意网页的Origin设置成自己域名的值服务器看到请求就会返回对应的Access-Control-Allow-Origin浏览器一对比发现一致直接放行。这类配置虽然不是最宽松的全通配但同样等于裸露所有来源都能跨域拿数据。更麻烦的是这种配置往往是“每个请求都反射”攻击者的Origin只要是动态生成的就能稳定触发。第三类是把Origin的校验写成了“包含匹配”或者“前缀匹配”。比如开发想允许example.com和自己公司的子域于是写出if (origin.endswith(example.com)) return origin;这种逻辑。看起来好像有校验实际上攻击者注册一个evilexample.com或者搞一个example.com.evil.com都能绕过。我在测试时经常用这种子域混淆、域名后缀伪造的方式命中率相当高。下面这个表格可以帮你直观对比这几种配置的区别配置模式响应头表现能否带凭据风险等级Access-Control-Allow-Origin: *任意Origin都能访问不能配合Credentials高但受限反射任意Origin请求Origin是什么就返回什么通常配合Credentialstrue严重Origin: null特殊处理直接返回Access-Control-Allow-Origin: null可以配合Credentialstrue严重后缀/前缀匹配Origin反射伪造的子域Origin通常能带凭据高危严格白名单比对只有精确匹配才返回头可安全配合Credentials低还有一种情况是处理Origin: null。如果页面是通过sandbox属性加载的iframe、使用file://协议或者直接打开本地HTML文件浏览器会发送Origin: null。有些服务器遇到 “null”就直接返回Access-Control-Allow-Origin: null结果攻击者可以构造一个带sandbox属性的iframe直接跨域读取目标数据。这种坑很少被注意到但一旦碰到接口在响应里返回了ACAO: null利用成本极低。2.3 为什么credentialstrue是放大器单独配了Access-Control-Allow-Origin: *或者反射Origin如果接口不涉及用户登录态危害相对有限。真正让CORS漏洞变得致命的是Access-Control-Allow-Credentials: true这个头。这个响应头的作用是允许浏览器在跨域请求中携带Cookie、HTTP认证信息和客户端TLS证书。通俗点说它允许跨域请求以“已登录用户”的身份访问接口。如果CORS配置根源上已经是任意Origin可反射再叠加这个头那么恶意网站发起的跨域请求就会自动带上受害者在目标网站的Cookie服务器识别为合法登录用户数据就被偷走了。这就是典型的“CORS信任链被完全攻破”场景。我在实战测试里会把“是否有Credentials”作为判断漏洞能否写严重的分水岭。一个只反射Origin但没带Credentials的接口只能读取不需要登录态的公开数据危害往往在中低危但如果带上了Credentials能读取用户个人信息那完全可以按高危甚至严重漏洞提交这也是我在给漏洞定级时最看重的一点。3. CORS跨域漏洞的检测与验证实操3.1 手工验证一个请求打天下最直接的检测方法就是给目标服务器发送一个带自定义Origin的跨域请求看响应里是否原样返回了对应头以及返回了哪些CORS相关响应头。我通常先用curl快速验证开一个带Cookie的请求行为越接近真实浏览器越好。curl -i -H Origin: https://evil.com https://target.com/user/info -H Cookie: sessionxxx重点看响应头里有没有这几项Access-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: trueVary: Origin如果Access-Control-Allow-Origin的值刚好等于我们发送的Origin: https://evil.com并且Access-Control-Allow-Credentials: true那基本可以直接断定为可稳定利用的漏洞。接下来再用浏览器实际验证一次确认能够读到响应体。只靠curl看响应头不算最完整的证据因为浏览器才是最终的裁判但curl可以快速筛选出可疑目标。有一个非常容易误判的点如果服务器没有返回Access-Control-Allow-Origin这不等于存在漏洞恰恰相反这通常是服务器的正常选择浏览器会直接拦截跨域响应。我在一些初次接触CORS测试的同学写的漏洞报告里经常看到他们拿“没有ACAO头”当漏洞来报这是不对的必须区分“服务器不主动允许”和“服务器错误允许”。前者是安全默认后者才是漏洞。3.2 用浏览器控制台完整复现curl验证通过之后我需要再证明恶意网页确实能拿到敏感数据这就得上浏览器了。在本地起一个恶意站点页面里写一段JavaScript用fetch请求目标接口然后查看控制台的网络面板和代码执行结果。// attacker.html fetch(https://target.com/user/info, { method: GET, credentials: include }) .then(res res.text()) .then(data { document.getElementById(out).textContent data; }) .catch(err { document.getElementById(out).textContent Fetch Error: err.message; });浏览器默认在跨域请求中不携带Cookie但加上credentials: include后浏览器就会带上Cookie。如果目标响应里同时存在Access-Control-Allow-Origin: http://evil.com和Access-Control-Allow-Credentials: true浏览器就会把响应数据交给页面上的JS攻击者就能直接读取到data的内容把它回传到自己的服务器。实际操作中我在验证时还会在恶意页面里加一个img或者XMLHttpRequest回传逻辑这样能演示完整的数据窃取链。需要注意的是fetch在跨域场景下还有一个预检流程。如果请求方法是GET、POST且Content-Type为text/plain或application/x-www-form-urlencoded等简单请求浏览器不会发OPTIONS预检但如果带着自定义请求头、使用application/json或使用PUT/DELETE等方法浏览器会先发一个OPTIONS预检请求。在测试时我需要同时验证预检和非预检两种情况因为有些配置错误只影响其中一种流程。3.3 常见测试技巧与工具辅助对于目标范围较大的测试纯手工点肯定效率太低我会用Burp Suite配合插件批量检测。思路是在抓包工具里修改请求包批量添加各种测试用的Origin值观察响应头的变化。下面这些测试值是我每测都会过一遍的基础集https://evil.comhttps://target.com.evil.comhttp://target.comnullhttps://evil.target.com如果你不确定目标的子域名解析规则就测一个看起来像子域但实际不存在的https://target.comevil.com另外Burp Suite社区的CORS插件、J2EEScan、CSP-Finder等都可以辅助收集带CORS响应头的接口加快审计节奏。但工具只能帮你找候选最终确认漏洞还是得靠手工验证和浏览器证据这个步骤不能省。还有一个偏门但好用的技巧去目标网站的JS文件里搜fetch(、XMLHttpRequest、axios把前端实际请求的接口列表整理出来再对这些接口批量做CORS测试。很多时候开发配置CORS是有选择性的只有少数接口带了跨域头直接扫描发现不了但前端代码里能找出所有接口逐个试一遍就能扩大战果。3.4 漏洞能否被利用的三个决定性条件这里必须多说一句CORS跨域漏洞能不能实际造成危害取决于三个条件的同时满足。第一个条件是接口返回的数据必须包含敏感信息。即使CORS配置完全失控但接口只返回公开的天气信息或版本号那只是低危甚至信息泄露都算不上。第二个条件是请求必须能带上受害者身份也就是Cookie或认证信息。我测试时会把Cookie带上看是否生效如果接口本身不用Cookie而用自定义Token或者本地存储那么单纯CORS配置错误加上credentials: include也拿不到用户的登录态这种情况下危害等级会大打折扣。第三个条件是浏览器环境下能够自由发起跨域请求并读取响应也就是前面说的响应头必须允许对应Origin并且不能触发浏览器拦截。我在写漏洞报告时会把这三点逐项列出来说明评审平台的高质量报告也都是这么分析的。你可以把这三个条件记成本能反应做CORS测试时先在脑子里过一遍少走很多弯路。4. 真实业务场景中的影响评估与利用思路4.1 攻击者通过CORS能窃取到什么在业务系统完整、用户数据齐全的站点里CORS配置错误可以导致攻击者以受害者身份读取任意有跨域配置的接口数据。举几个真实场景电商站点的用户接口/user/order/list返回订单列表包含收件人姓名、手机号、收货地址。如果接口允许任意Origin且带credentials攻击者只需要诱导受害者打开恶意网页页面里的JavaScript就会自动带上受害者的登录Cookie去请求订单接口再把返回的JSON数据回传给攻击者服务器。我测试过一些站点通过这种手段拿到的数据细节甚至比CSRF更完整因为CSRF只能发请求但读不到响应而CORS能让攻击者直接看到服务器返回的所有内容。企业内部系统的用户资料接口、财务系统的交易记录接口、SaaS平台的API Key管理接口都是CORS漏洞的高价值目标。遇到了能读取到这些数据的CORS配置问题直接往严重漏洞方向写。有些场景里CORS还能配合子域名接管、XSS漏洞做组合利用比如某个主域名信任了某子域名而那个子域名本身可以被攻击者控制那么攻击者就能以被信任子域名为跳板绕过主域名的CORS校验读取主域名数据。4.2 CORS与CSRF、XSS的联动利用说一个实战价值很高的组合拳思路CORS漏洞的核心能力是“跨域读取响应”CSRF的核心能力是“跨域发送请求”XSS的核心能力是“在受害者页面执行任意脚本”。这三者单独拎出来都有一定限制但组合起来经常能把一条很弱的线索放大成严重问题。举个例子某个接口https://api.example.com/v1/users配置了反射Origin并且带credentials。攻击者可以做如下攻击链在恶意页面上用js向https://api.example.com/v1/users发起跨域请求拿到受害者个人信息后再把数据POST到攻击者的服务器。整条链路里没有任何步骤需要执行受害者站点的脚本完全靠CORS配置漏洞打通了跨域数据读取。如果再把目标换成能修改数据的接口比如修改邮箱、重置密码之类的攻击者就能直接操作受害者账户。另外有些业务在内网部署了运维后台虽然外部不可达但如果后台的某个HTML页面存在XSS攻击者可以利用XSS在浏览器上下文中发起同源请求。此时即使后台没有配置CORSXSS脚本也能读取响应。这种情况下CORS不再必要因为XSS已经在同源环境里执行了。所以我的经验是CORS漏洞经常是锦上添花型放大而XSS是釜底抽薪型直取。在评估一个系统的综合风险时应该把CORS配置纳入整体攻击面来考虑不要仅孤立地看单个漏洞。4.3 如何评估CORS漏洞的严重等级根据我在SRC平台提交和评审漏洞的经验CORS漏洞评级主要看下面几个因素因素低危倾向严重倾向请求是否携带凭据不携带携带Cookie/Token返回数据敏感度公开信息个人敏感数据、订单、后台配置影响范围单接口全站或全API网关接口是否需要额外条件需要用户点击链接访问即触发可读取性响应无法直接读取响应可直接用JS读取如果只想快速给一个参考值仅反射Origin但没有Access-Control-Allow-Credentials: true一般中低危反射Origin且带Credentials能读取敏感数据高危起步如果接口还能写操作、影响大量用户直接报严重。这里也要说句大实话真实测试时不是所有“CORS配置错误”都值得花时间深入有些接口数据本来就是公开的配置再宽松也只是低危把有限的时间花在高价值接口上产出比高得多。5. 漏洞修复与加固实践从配置根源解决问题5.1 最根本的修复思路精确白名单CORS跨域漏洞的核心问题不是“不能有跨域”而是“不该允许的来源被允许了”。所以修复方案的第一性原理就是只允许明确已知的合法来源绝不反射任意Origin也绝不用通配符配合凭据。正确的做法是在后端维护一个来源白名单把请求里的Origin与白名单逐一精确匹配只有完全一致才在响应里返回对应的Access-Control-Allow-Origin否则不返回这个响应头。同时要设置Vary: Origin避免CDN或者浏览器缓存把某个Origin对应的响应头错误地返回给另一个Origin。白名单匹配时要注意几个容易踩的坑。不能用字符串包含去判断example.com会被evilexample.com绕过不能用正则里的.当通配符需要转义为\.要区分协议https://api.example.com与http://api.example.com来源不同如果只需要HTTPS就把HTTP排除掉。我见过一个项目使用PHP代码写校验时直接用strpos($origin, example.com) ! false就判断为允许这种写法等于把整个域名体系都暴露给了任何包含example.com的恶意域名比如example.com.attack.com非常危险。下面这段伪代码展示了规范的白名单匹配逻辑// 伪代码白名单精确匹配 $allowList [https://www.example.com, https://admin.example.com]; $origin $_SERVER[HTTP_ORIGIN] ?? ; if (in_array($origin, $allowList, true)) { header(Access-Control-Allow-Origin: . $origin); header(Vary: Origin); } // 不在白名单里则什么都不返回以FastAPI为例正确配置CORS中间件的代码如下注意我没有使用allow_origins[*]而是精确列出了允许的来源from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware app FastAPI() origins [ https://www.example.com, https://admin.example.com, ] app.add_middleware( CORSMiddleware, allow_originsorigins, allow_credentialsTrue, allow_methods[GET, POST, PUT, DELETE], allow_headers[Content-Type, Authorization], )在Flask里可以用Flask-CORS库也可以在视图函数里手动设置响应头。手动设置的好处是可控性更强尤其适合接口数量不多的小项目。Node.js的Express里我一般用cors中间件传入一个函数动态返回允许来源这和FastAPI的配置思路一致。Spring Boot的CrossOrigin注解和CorsConfiguration类也同理不要用allowedOrigins(*)加allowCredentials(true)的组合这是Spring中明确禁止的启动时会直接报错。5.2 必须避开的几个错误配置组合修复CORS时我整理了一个高频错误清单任何一个都能让修复形同虚设第一个是Access-Control-Allow-Origin: *加Access-Control-Allow-Credentials: true。标准上都明确不允许这种组合浏览器看到带credentials的通配符会直接拒绝响应。但有些旧框架或网关会自动补上credentials导致配置失效修复时要把这两个头都检查一遍。第二个是动态生成Access-Control-Allow-Origin时把非法来源也放进了白名单。有些人的代码逻辑是先判断再拼接但判断写错了。比如允许来源列表来自配置但配置里写了一个废弃的旧子域这个子域如果早就被释放或被攻击者接管攻击者就可以通过劫持一个被信任的子域来绕过CORS防护。所以在维护白名单时要定期清理不再使用的子域名或内部域名。第三个是信任了不该信任的第三方域名。有些业务为了对接外部合作伙伴而临时加了某个域名的跨域权限后来合作结束但配置一直没删。我在测试的时候就专门找这种历史遗留的第三方域名它的安全水位往往远低于主站一旦它先被攻破主站的CORS防护也就跟着被绕过。第四个是使用Access-Control-Allow-Origin: null来兼容本地开发。这个我在前面提过Origin: null可以被任何sandbox iframe伪造宁可让本地开发走代理也绝不能在生产环境允许null来源。修复时不仅要把响应头配置改掉还要检查有没有反向代理或WAF规则对第三方的数据报文做修改把非法请求的Origin头抹掉。有些代理为了方便认为会把Origin: null或非允许来源的请求转发给上游时不带Origin头这会让后端一层防护直接失效。这里需要特别确认的是检查网络链路层行为一般只需要关注Web服务器和接入层的配置不要把范围扩大到系统网络本身。如果测试环境有相关设备可以重点核查接入层是否对请求头做了重写规则。5.3 全链路配置建议Nginx、CDN与后端一起改我修复过很多CORS漏洞最大的教训是只改应用后端远远不够全链路都得检查。Nginx做反向代理时如果应用本身已经正确返回CORS响应头但Nginx配置里又叠加了一层add_header Access-Control-Allow-Origin http://www.example.com;那么所有经过Nginx的请求都会收到这个头后端白名单就形同虚设。更麻烦的是Nginxadd_header的生效范围和location层级有关很容易出现“有的接口加了有的接口没加”的混乱状态。正确做法是全链路只在一个层级设置CORS响应头其余层级透传不要层层设置。另外如果站点用了CDNCDN通常会对Access-Control-Allow-Origin、Vary等响应头做缓存。如果CDN缓存了某个Origin对应的CORS响应再返回给别的Origin就会产生安全和功能双重问题。这时候一定要在CDN层把Vary: Origin纳入缓存Key计算或者干脆把CORS响应头放到CDN源站控制让CDN跟随源站。还有一点容易忽略如果业务接口跨域请求需要携带Cookie那么Access-Control-Allow-Origin的值绝不能是*必须精确返回白名单里的那个Origin值并且同时返回Access-Control-Allow-Credentials: true。这两个头缺一不可。很多人修CORS时只加了ACAO发现请求还是带不上Cookie然后又在响应头里补上credentials结果发现浏览器仍然拦截原因往往是ACAO返回的是*而不是具体Origin这组组合必须成对出现才行。5.4 配置安全帽上线的验证与巡检修完配置之后至少要重复一遍第一节里的所有验证步骤确认以下内容请求Origin: https://evil.com时响应里没有Access-Control-Allow-Origin: https://evil.com请求Origin: https://www.example.com合法的时响应里有对应的ACAO且值为该合法来源请求Origin: null时响应里没有Access-Control-Allow-Origin: null请求合法Origin且返回credentials时页面JS能正常读取数据响应头里有Vary: Origin我自己的习惯是把上述验证脚本写成一段自检测试修复后跑一遍上线后再定期跑一遍。对于体量较大的站点可以用Python写一个简单的巡检脚本定时检查所有重要接口的CORS响应头是否符合白名单策略。下面是一个示例import requests ORIGIN_TEST https://evil.com TARGETS [ https://www.example.com/api/user/info, https://www.example.com/api/order/list, ] ALLOWED_ORIGIN https://www.example.com session requests.Session() for url in TARGETS: r session.get(url, headers{Origin: ORIGIN_TEST}) acao r.headers.get(Access-Control-Allow-Origin, ) if acao ORIGIN_TEST: print(f[VULN] {url} reflects arbitrary Origin: {acao}) else: print(f[OK] {url} does not reflect arbitrary Origin)6. 常见问题与排查技巧实录6.1 明明改了配置浏览器还是报CORS错很多次修完CORS后开发反馈“配置肯定改了但前端还是报错”。这时候不要急着怀疑代码优先检查浏览器缓存。浏览器对CORS响应是有缓存的特别是带Access-Control-Max-Age头的预检响应缓存时间甚至可能是10分钟或更久。我在测试时经常遇到这个坑改了服务端配置但前面已经有一个旧响应被缓存了浏览器直接用缓存里的旧策略拦截看起来就像没改一样。处理方式很简单在浏览器无痕窗口里测试或者清掉这个域名的缓存。使用DevTools的Network面板时勾选“Disable cache”也能有效避免缓存干扰。如果你是用fetch测试还可以在请求里加一个随机参数破坏缓存命中的URL匹配效果也一样。如果是后端开发在排查线上问题还得多检查一层看发出去的请求是否命中CDN缓存节点。早先在部分CDN边缘节点上对OPTIONS预检请求的处理是有缺陷的会导致预检响应头丢失。遇到这种情况需要刷新CDN缓存或者调整CDN上针对OPTIONS请求的缓存策略而不是反复改后端代码。6.2 加了多个自定义请求头预检直接凉了前端调用接口时如果带了自定义头比如Authorization、X-Requested-With或者X-CSRF-Token浏览器会先发OPTIONS预检请求预检响应需要通过Access-Control-Allow-Headers明确允许这些头后续的真实请求才会发出去。我见过好多次CORS配置里只写了Access-Control-Allow-Origin完全没管Headers导致预检阶段就挂了前端一直在报错。排查时先用curl模拟OPTIONS请求重点看响应头里的Access-Control-Allow-Headers和Access-Control-Allow-Methodscurl -i -X OPTIONS https://target.com/api/user/info \ -H Origin: https://www.example.com \ -H Access-Control-Request-Method: GET \ -H Access-Control-Request-Headers: Authorization然后对比前端实际携带的自定义头是否都被允许。如果漏了在后端CORS配置里把需要的头加进allow_headers列表。另外提醒一下写的时候不要图省事把Access-Control-Allow-Headers设成*然后还开allow_credentialstrue这也是标准上不允许的组合浏览器会拒绝。正确做法是列出项目实际用到的所有自定义头。6.3 测试结论容易忽略的一个细节请求的“简单性”我在讲CORS利用时一直强调“简单请求”和“预检请求”的区别。测试时如果只测了带application/json和自定义头的请求可能忽略了最简单的GET请求反之如果只测简单请求也可能忽略了预检请求下的配置差异。稳妥的做法是两类请求都覆盖一遍记录下两类请求各自的响应头。有一个真实发生过的事情某系统的CORS配置在预检请求上出现了问题开发顺手在Nginx层把所有OPTIONS请求的响应都统一加上了Access-Control-Allow-Origin: *和Access-Control-Allow-Headers: *好让预检通过。我当时测的时候发了一个简单的GET请求不带自定义头根本不触发预检流程但响应里因为Nginx全局配置带了Access-Control-Allow-Origin: *配合接口自身的Access-Control-Allow-Credentials: true直接在简单请求上读到了数据。这种配置不一致造成的漏洞漏洞很隐蔽靠常规预检测试根本发现不了必须同时测两类请求才看得清楚。6.4 我踩过的一个真实坑子域信任链被反向利用最后分享一个我记忆深刻的案例。做某个SRC项目时主站example.com的CORS白名单包含tools.example.com和dev.example.com两个子域名。我用常规反射Origin的手法试了下主站没有任何CORS头返回看起来防御做得挺规范。但我深入测了一下这两个被信任的子域发现dev.example.com上挂着的是一个很老的管理后台存在存储型XSS漏洞。我把XSS脚本写入后台的某个输入框当管理员打开后台页面时脚本在主站同域策略的信任关系下执行向主站接口发起了跨域请求。因为主站信任dev.example.comCORS响应头正常返回XSS脚本顺利读取了接口数据。这件事让我彻底明白CORS白名单里每一个被信任的来源都在无形中扩展了攻击面。修复CORS漏洞时不光要看配置本身还要看看名单里的域名是否安全、是否存在被接管的风险。如果一个子域已经废弃但没有从白名单里删除甚至域名解析都已经被释放攻击者可以直接注册这个域名让整个CORS白名单保护形同虚设。在做CORS加固时我会顺手把所有白名单域名的解析记录、备案状态、历史漏洞都过一遍把不安全的子域剔除出去。7. 写在最后的一点个人经验CORS跨域访问漏洞说到底是“信任边界”弹得太大导致的问题。它不像SQL注入那样能直接拖库也不像RCE那样能直接控制服务器但它给攻击者提供了一条非常顺滑的“跨域读数据”通道在真实业务中的影响不容小觑。我自己做漏洞挖掘时会把CORS列入登录接口、用户中心、订单接口等核心页面必测的清单里因为这类接口但凡配错往往能直接拿到高价值数据。如果你是在做SRC漏洞挖掘我给你的建议是测出CORS漏洞后千万别只写“存在CORS配置错误”就完事要按前面提到的三个决定条件把能读取的接口、返回的数据样例、受害者的触发方式都写成证据链。哪怕只是一个看起来普通的反射Origin只要能证明可以读回含有手机号或订单的数据评审都会给不错的评级。我见过不少同学因为在报告里只贴了响应头截图导致原本高危的问题被降级非常可惜。如果你是在做开发或运维我给你的建议是CORS配置不要自己手搓正则不要图快用通配符老老实实维护白名单列表每次上线前把上面那套自检测试脚本跑一遍顺手检查一下响应头里的Vary: Origin。很多安全问题就出在贪图省事的五分钟里而修复它可能只需要认真读完这篇文章的时间。