同源策略与跨域资源共享(CORS)详解
字数 3075 2025-11-06 12:41:12

同源策略与跨域资源共享(CORS)详解

一、知识点描述
同源策略(Same-Origin Policy)是浏览器最核心的安全基础,它限制一个源(origin)的文档或脚本如何与另一个源的资源进行交互。这是一个关键的隔离机制,能有效防止恶意网站窃取数据。跨域资源共享(CORS)则是一种基于HTTP头部的机制,允许服务器指示浏览器,哪些外部源有权访问其资源,从而在保证安全的前提下,合法地绕过同源策略的限制。理解这两者是如何协同工作的,对于现代Web应用开发至关重要。

二、循序渐进讲解

第一步:深入理解“源”(Origin)

  1. 定义:一个“源”由三部分组成:协议域名(或IP地址)端口
  2. 判断逻辑:只有当两个URL的协议、域名、端口完全相同时,浏览器才认为它们是“同源”的。任何一项不匹配,则被视为“跨域”。
  3. 举例说明
    • 假设当前页面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 (域名不同,www vs api)
      • https://www.example.com:8080/docs/page.html (端口不同,443 vs 8080)

第二步:同源策略的具体限制行为
同源策略主要控制以下三类行为:

  1. DOM访问限制:禁止不同源的页面通过JavaScript访问彼此的DOM。例如,iframe内嵌了一个跨域页面,父页面的JS无法读取或修改iframe内页面的内容。
  2. 网络请求(Ajax/Fetch)限制:默认情况下,使用XMLHttpRequestFetch API发起的跨域HTTP请求会被浏览器拦截。这是开发中最常遇到的问题。
  3. 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将请求分为两类:简单请求非简单请求

  1. 简单请求

    • 条件:请求方法为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(或使用通配符 * 表示允许任何源)。浏览器检查响应头,如果匹配则允许前端应用读取响应,否则报错。
  2. 非简单请求(需预检的请求)

    • 触发条件:使用了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. 实际请求
        • 只有预检请求通过后,浏览器才会发出真正的实际请求。实际请求和响应过程与简单请求类似,同样需要包含 OriginAccess-Control-Allow-Origin 等头。

第五步: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-MethodsAccess-Control-Allow-Headers 中只列出必需的项。

通过理解同源策略的限制和CORS的协商机制,你就能在开发中正确处理跨域问题,并确保API接口的安全配置。

同源策略与跨域资源共享(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 ( 域名 不同, www vs api ) https://www.example.com:8080/docs/page.html ( 端口 不同,443 vs 8080) 第二步:同源策略的具体限制行为 同源策略主要控制以下三类行为: 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 (或使用通配符 * 表示允许任何源)。浏览器检查响应头,如果匹配则允许前端应用读取响应,否则报错。 非简单请求(需预检的请求) : 触发条件 :使用了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 等头。 第五步: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接口的安全配置。