同源策略与跨域资源共享(CORS)详解
字数 3075 2025-11-06 12:41:12
同源策略与跨域资源共享(CORS)详解
一、知识点描述
同源策略(Same-Origin Policy)是浏览器最核心的安全基础,它限制一个源(origin)的文档或脚本如何与另一个源的资源进行交互。这是一个关键的隔离机制,能有效防止恶意网站窃取数据。跨域资源共享(CORS)则是一种基于HTTP头部的机制,允许服务器指示浏览器,哪些外部源有权访问其资源,从而在保证安全的前提下,合法地绕过同源策略的限制。理解这两者是如何协同工作的,对于现代Web应用开发至关重要。
二、循序渐进讲解
第一步:深入理解“源”(Origin)
- 定义:一个“源”由三部分组成:协议、域名(或IP地址) 和端口。
- 判断逻辑:只有当两个URL的协议、域名、端口完全相同时,浏览器才认为它们是“同源”的。任何一项不匹配,则被视为“跨域”。
- 举例说明:
- 假设当前页面URL为:
https://www.example.com:443/docs/page.html - 同源:
https://www.example.com:443/docs/another.html(协议、域名、端口均相同) - 不同源(跨域):
http://www.example.com/docs/page.html(协议不同,HTTP vs HTTPS)https://api.example.com/docs/page.html(域名不同,wwwvsapi)https://www.example.com:8080/docs/page.html(端口不同,443 vs 8080)
- 假设当前页面URL为:
第二步:同源策略的具体限制行为
同源策略主要控制以下三类行为:
- DOM访问限制:禁止不同源的页面通过JavaScript访问彼此的DOM。例如,
iframe内嵌了一个跨域页面,父页面的JS无法读取或修改iframe内页面的内容。 - 网络请求(Ajax/Fetch)限制:默认情况下,使用
XMLHttpRequest或Fetch API发起的跨域HTTP请求会被浏览器拦截。这是开发中最常遇到的问题。 - Cookie、LocalStorage和IndexedDB访问限制:浏览器只允许页面读取同源的站点存储的数据。
重要例外:有些HTML标签天生允许跨域加载资源,例如 <img>, <script>, <link>, <iframe> (注意:是加载资源,而非访问其DOM)。这是JSONP等技术的基础,但也可能带来安全风险。
第三步:为什么需要CORS?
现代Web应用通常是前后端分离的架构。前端应用可能部署在 https://webapp.com,而后端API服务器在 https://api.service.com。根据同源策略,前端无法直接调用后端的API。为了解决这个合法需求,W3C制定了CORS标准,允许服务器明确地声明哪些外部源可以访问自己的资源。
第四步:CORS的工作原理(核心是“预检请求”)
CORS将请求分为两类:简单请求和非简单请求。
-
简单请求:
- 条件:请求方法为GET、HEAD、POST之一,且请求头仅限于有限的几种(如
Accept,Accept-Language,Content-Language,Content-Type的值仅限于application/x-www-form-urlencoded,multipart/form-data,text/plain)。 - 过程:浏览器会直接发出请求,但在请求头中自动添加一个
Origin字段,标明请求的来源(例如Origin: https://webapp.com)。服务器收到后,如果允许该源访问,则应在响应头中包含Access-Control-Allow-Origin: https://webapp.com(或使用通配符*表示允许任何源)。浏览器检查响应头,如果匹配则允许前端应用读取响应,否则报错。
- 条件:请求方法为GET、HEAD、POST之一,且请求头仅限于有限的几种(如
-
非简单请求(需预检的请求):
- 触发条件:使用了PUT、DELETE等方法,或Content-Type为
application/json,或包含了自定义头(如Authorization)。 - 过程(分为两步):
- a. 预检请求 (Preflight Request):
- 在实际请求发出之前,浏览器会自动使用 OPTIONS 方法发起一个预检请求。
- 这个请求的
Origin头同样标明来源。 - 它还会包含两个特殊头:
Access-Control-Request-Method: 声明即将发出的实际请求所使用的方法(如PUT)。Access-Control-Request-Headers: 声明即将发出的实际请求所携带的自定义头(如Authorization)。
- b. 服务器响应预检:
- 服务器需要正确响应这个OPTIONS请求。
- 响应头中必须包含:
Access-Control-Allow-Origin: 允许的源。Access-Control-Allow-Methods: 允许的实际请求方法(如PUT, POST)。Access-Control-Allow-Headers: 允许的实际请求头(如Authorization)。
- 还可以包含
Access-Control-Max-Age,指示预检结果的有效期,避免重复预检。
- c. 实际请求:
- 只有预检请求通过后,浏览器才会发出真正的实际请求。实际请求和响应过程与简单请求类似,同样需要包含
Origin和Access-Control-Allow-Origin等头。
- 只有预检请求通过后,浏览器才会发出真正的实际请求。实际请求和响应过程与简单请求类似,同样需要包含
- a. 预检请求 (Preflight Request):
- 触发条件:使用了PUT、DELETE等方法,或Content-Type为
第五步:CORS相关的其他重要头部
Access-Control-Allow-Credentials: 当请求需要携带Cookie或HTTP认证信息时(需要设置请求的credentials: 'include'),服务器必须将此头设为true。并且Access-Control-Allow-Origin不能为通配符*,必须是明确的源。Access-Control-Expose-Headers: 允许前端JavaScript访问的响应头白名单。默认情况下,前端只能访问几个基本的响应头(如Cache-Control, Content-Language, Content-Type等)。
总结与防御视角
- 同源策略是浏览器施加的“默认拒绝”策略,是保护用户的第一道防线。
- CORS是服务器控制的“选择性允许”策略。服务器开发者必须谨慎配置CORS头。
- 错误配置的风险:将
Access-Control-Allow-Origin设置为*虽然方便,但意味着任何网站都可以通过前端脚本访问你的资源(如果请求不需要凭证)。最安全的做法是维护一个允许的来源白名单。 - 对于需要凭证的请求,绝不可使用
*。 - 根据最小权限原则,在
Access-Control-Allow-Methods和Access-Control-Allow-Headers中只列出必需的项。
- 错误配置的风险:将
通过理解同源策略的限制和CORS的协商机制,你就能在开发中正确处理跨域问题,并确保API接口的安全配置。