CORS (跨源资源共享)
约 4 分钟阅读
最后更新: 2026-09-01
什么是 CORS (跨源资源共享)
CORS (Cross-Origin Resource Sharing) 是一种基于 HTTP 头的机制,用于安全地放宽浏览器的同源策略,实现不同源之间的资源共享。源由协议 (http/https)、主机名和端口号三个要素的组合定义。
同源策略是浏览器的安全机制,限制从一个源加载的脚本读取另一个源的响应。如果没有这个限制,恶意网站就可以通过用户的浏览器调用用户已登录的银行网站的 API,并用自己的脚本读取响应中包含的余额和交易记录。
这里需要理清的关系是:默认进行防护的是同源策略一侧,而 CORS 是用于有选择地放宽该限制的机制。把它理解为「配置 CORS 来阻止跨源攻击」,方向正好相反,实际上这是把默认关闭的东西只按必要范围打开的操作。还有一点,同源策略阻止的是响应的读取,而不是请求的发送。发送本身仍然会成立,这正是 CSRF 至今仍是现实威胁的原因。
然而,在现代 Web 应用中,前端和后端 API 运行在不同域名上是很常见的。CORS 通过服务器在 HTTP 响应头中明确声明「允许哪些源的访问」,安全地实现必要的跨源通信。
CORS 的工作机制
CORS 根据请求类型以两种方式工作。
简单请求 (Simple Request)
当方法为 GET、HEAD、POST 之一,附加的头处于 Accept、Accept-Language、Content-Language、Content-Type 这类安全列表的范围内,且 Content-Type 的值为 application/x-www-form-urlencoded、multipart/form-data、text/plain 之一时,该请求被视为「简单请求」。反过来说,只要多加一个 Authorization 或 X-Requested-With 这样的自定义头,就会脱离这个条件。
在简单请求中,浏览器直接发送请求,并检查响应中的 Access-Control-Allow-Origin 头。如果源被允许,则将响应传递给 JavaScript;否则阻止读取。被阻断的只是来自 JavaScript 的读取,请求已经到达服务器,处理也已经执行。即使浏览器控制台显示 CORS 错误,服务器端也可能已经登记或更新了数据,在开发过程中排查问题时务必意识到这个区别。
预检请求 (Preflight Request)
对于不满足简单请求条件的请求 (如 PUT、DELETE 或包含自定义头的请求),浏览器在实际请求之前使用 OPTIONS 方法发送「预检请求」。服务器通过以下头响应允许条件。
Access-Control-Allow-Origin:允许的源Access-Control-Allow-Methods:允许的 HTTP 方法Access-Control-Allow-Headers:允许的请求头Access-Control-Max-Age:预检结果的缓存时间 (秒)
如果预检响应满足允许条件,浏览器才发送实际请求。
携带凭据的请求
包含 Cookie 或认证头的跨源请求需要 Access-Control-Allow-Credentials: true。此时,Access-Control-Allow-Origin 不能使用通配符 (*),必须指定具体的源。通配符仅对不携带凭据的请求有效,这是规范层面的互斥,不存在让两者同时成立的写法。Access-Control-Allow-Methods 和 Access-Control-Allow-Headers 中的 * 也一样,在携带凭据的请求中不会被当作通配符。
另一个陷阱是预检本身不携带 Cookie (规范规定预检的凭据模式始终为 same-origin)。如果在服务器或反向代理的配置中对 OPTIONS 请求也要求认证,就会在到达实际请求之前失败,形成原因难以看清的 CORS 错误。
常见配置错误与安全风险
CORS 配置错误会使同源策略的保护失效,造成严重的安全风险。
- 随意使用
Access-Control-Allow-Origin: *:允许所有源意味着任何网站都可以访问 API。除公开 API 外不应使用。处理认证信息的 API 绝对不能使用 - 未经验证直接反映 Origin 头:将请求的 Origin 头值直接反映到
Access-Control-Allow-Origin中,实质上等同于通配符。应通过白名单验证 - 允许 null 源:允许
Access-Control-Allow-Origin: null会使沙箱化的 iframe 和本地文件可以访问,可能被攻击利用 - 过于宽松的子域许可与后缀匹配:使用
*.example.com这样的模式匹配时,如果攻击者能够接管子域,就可以利用 CORS。更进一步,只检查字符串结尾是否一致的实现,还会放行攻击者自己注册的evil-example.com这类另外的域名
CORS 配置错误与 XSS 或 CSRF 结合时,可能扩大危害。另一方面,即使把 CORS 配置得再严格也无法防止 CSRF。像表单提交这样的简单请求不会触发预检就能到达服务器,因此 CSRF 需要用令牌或 Cookie 的 SameSite 属性另行防护。
安全 CORS 配置的最佳实践
安全配置 CORS 的指南。
- 通过白名单管理允许的源:在环境变量或配置文件中维护允许源列表,与请求的 Origin 头进行精确匹配验证
- 仅允许最少必要的方法和头:在
Access-Control-Allow-Methods和Access-Control-Allow-Headers中只列出实际使用的内容 - 利用预检缓存:明确设置
Access-Control-Max-Age,减少预检请求的频率。未指定时规范上的默认值只有 5 秒,实际上几乎每个请求之前都会发出一次 OPTIONS。不过浏览器端存在缓存时间的上限,超过上限的值会被削减到上限,因此写极长的秒数也不会延长效果 - 谨慎处理携带凭据的请求:设置
Access-Control-Allow-Credentials: true时,严格限制允许的源 - 与 CSP 和安全头配合使用:将 CORS 与其他安全头结合,构建纵深防御
- 以 HTTPS 为前提:不在允许列表中包含 HTTP 源。防止通过中间人攻击伪造源
CORS 是浏览器端的控制,不适用于服务器间通信或 curl 这类命令行工具的请求。对于攻击者从自己的环境直接调用 API 的路径没有效果,因此不要仅依赖 CORS,务必实现服务器端的认证和授权。
另外,CORS 先是作为 W3C 建议规范「Cross-Origin Resource Sharing」(2014 年 1 月 16 日) 标准化,之后被并入 WHATWG 的 Fetch 标准。确认细节行为时,参照 Fetch 标准更为准确。
常见误解
- CORS 是保护服务器的安全功能
- CORS 是浏览器端的控制,不直接保护服务器。curl 或服务器间通信不受 CORS 限制。本来禁止读取其他源响应的是同源策略,CORS 是放宽该限制一侧的机制。它是保护浏览器用户的一层,与服务器端的认证和授权处于不同层面。
- 出现 CORS 错误用通配符 (*) 解决就好
- 通配符允许所有源的访问,安全风险很高。携带凭据的请求不能使用通配符。正确的做法是仅将必要的源添加到白名单中。