首页 / 帮助文档 / 后端开发跨域安全配置规避访问风险

后端开发跨域安全配置规避访问风险

跨域配置错误导致的安全事件,绝大多数不是攻击者用了多高深的技术,而是后端开发者在“方便调试”和“快速上线”之间,把本该收紧的访问策略放开了。一个配置项写错,就可能让恶意站点直接读取用户登录态、调用内部接口,甚至伪造请求篡改数据。要规避这类风险,核心不是背熟CORS规范,而是理解浏览器同源策略的边界,以及后端在响应头里到底放行了什么。

先厘清一个常见误区:跨域限制是浏览器行为,不是服务器行为

很多开发者以为后端配置了CORS之后,服务器就会拦截非法跨域请求。实际上,请求照样会发到服务器,后端逻辑照常执行,数据库该改的也改了。浏览器只是根据响应头决定是否把结果交给前端脚本。这意味着,如果你的接口只靠CORS来“保护”,那等于没有保护——攻击者用curl、Postman或自写脚本直接请求,根本不受同源策略约束。所以跨域安全配置的第一原则是:CORS解决的是“允许哪些前端页面读取响应”,而不是“允许哪些客户端调用接口”。真正的访问控制必须依赖身份认证、权限校验和CSRF防护。

Access-Control-Allow-Origin 的三种危险写法

第一种是直接反射Origin。很多后端框架的“快速配置”示例会写成这样:

response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

这等于告诉浏览器:任何网站都可以读取这个接口的响应。如果接口涉及用户数据,恶意站点只要诱导已登录用户访问,就能在自己的页面里用fetch读取到完整响应。第二种是配置多个固定域名但用了不严谨的匹配逻辑,例如只判断Origin字符串是否包含某个域名片段,导致 evil-example.com 也能通过 example.com 的校验。第三种是配置了 *.example.com 这样的通配符,但实际业务中某个子域已经被废弃或被第三方接管,废弃子域上的XSS漏洞会直接变成主站数据泄露的跳板。

Access-Control-Allow-Credentials 与通配符的组合陷阱

当接口需要携带Cookie或HTTP认证信息时,后端必须设置:

Access-Control-Allow-Credentials: true

但规范明确规定,此时 Access-Control-Allow-Origin 不能使用 * 通配符,必须指定具体Origin。部分开发者在测试环境用 * 加 credentials 的组合能跑通,是因为某些浏览器旧版本或插件环境宽松,生产环境一旦遇到严格遵循规范的浏览器,请求会直接失败。更危险的是,有些服务端框架在检测到 credentials 为 true 时,会自动把 Origin 反射回去,开发者以为自己配置的是固定域名,实际线上行为却变成了反射任意Origin。上线前务必用 curl 实测响应头,而不是只看配置文件。

预检请求的缓存时间与安全的关系

非简单请求会先发一个OPTIONS预检请求,浏览器根据 Access-Control-Max-Age 决定缓存预检结果多久。这个值设置得过大,比如一年,会导致后端修改CORS策略后,用户浏览器仍然沿用旧的宽松策略。对于已经泄露过数据的接口,即使后端紧急收紧配置,老用户浏览器里缓存的预检结果依然允许恶意页面继续读取,直到缓存过期。建议生产环境将 Max-Age 控制在600秒以内,既能减少预检请求开销,又能在策略变更时快速生效。

除了CORS,还要堵住JSONP这个隐蔽通道

很多老项目里还保留着JSONP接口。JSONP本质上是通过script标签加载远程脚本,它不受同源策略限制,也不需要任何CORS响应头。如果后端同时提供了JSONP和CORS两种跨域方案,攻击者会优先选择JSONP,因为它完全绕过了CORS的配置限制。更麻烦的是,JSONP接口往往缺乏对回调函数名的严格校验,如果回调名直接拼进响应体,就可能产生反射型XSS。对于已经不再需要支持老旧浏览器的业务,应直接下线JSONP接口;如果必须保留,回调函数名只允许字母数字和下划线,且长度限制在64字符以内。

WebSocket跨域与Origin校验

WebSocket握手阶段同样会携带Origin头,但很多后端WebSocket服务只校验了协议升级和Sec-WebSocket-Key,完全忽略了Origin。浏览器发起的WebSocket连接不受CORS策略约束,只要服务端不校验Origin,任何网页都能连上你的WebSocket服务并收发消息。如果WebSocket承载了用户实时数据或指令下发功能,必须显式校验握手请求的Origin是否在允许列表内。校验逻辑要放在握手阶段,而不是连接建立之后,因为一旦握手成功,后续数据帧里不再携带Origin信息。

代理层与网关的跨域配置覆盖问题

大型项目里,CORS配置可能同时存在于应用代码、Nginx、API网关三层。实际生效的是最终返回给浏览器的响应头,如果网关层统一追加了 Access-Control-Allow-Origin: *,而应用层又配置了带Credentials的固定Origin,最终响应头里会出现两个互相矛盾的Allow-Origin。浏览器对重复响应头的处理方式并不统一,部分浏览器会取第一个,部分会拒绝请求。更危险的是,网关层为了“方便”统一配置了反射Origin,导致所有后端服务的CORS策略形同虚设。排查时要用curl -I查看最终响应头,确认没有多层配置叠加。

针对跨域配置的测试清单

上线前至少要做四类测试。第一,用恶意Origin发送请求,确认响应头中 Access-Control-Allow-Origin 不会反射该Origin,且响应体不会被浏览器脚本读取。第二,携带真实用户Cookie从合法Origin请求,确认 Access-Control-Allow-Credentials 为true且Allow-Origin精确匹配。第三,从合法Origin的子域名或相似域名请求,确认不会因为前缀匹配或包含匹配而误放行。第四,对OPTIONS预检请求检查 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers,确认没有放行PUT、DELETE等高风险方法,也没有放行自定义头里可能携带的敏感信息。

跨域安全配置的最终原则

把CORS配置当成访问控制的一部分来审计,而不是当成“解决前端报错”的临时手段。每次前端报跨域错误时,正确流程是先确认这个Origin是否真的需要访问该接口,再决定是否加入允许列表,而不是直接改成 * 或反射Origin。允许列表要定期审查,已经下线的子域、测试域名、第三方合作方域名要及时移除。对于需要携带凭证的接口,尽量收敛到最小范围,能不用Cookie就不用,改用Authorization头携带Token,这样即使CORS配置失误,攻击者拿不到Token也无法读取数据。跨域安全配置的核心不是“让请求通”,而是“只让该通的请求通”。