跨域:浏览器到底拦住了什么

跨域的根本问题

先假设一个危险的情况:网站 A 可以随意读取网站 B 的响应。

用户已经登录银行,浏览器可能带着银行的 Cookie。此时,恶意网页只需要调用银行的余额、转账记录接口,再把响应发给攻击者,用户的敏感数据就泄露了。

所以,浏览器的**同源策略(Same-Origin Policy)**存在的核心目的,是阻断这种跨网站读取敏感数据的行为。

CORS(Cross-Origin Resource Sharing),则是服务器向浏览器声明的、受规则约束的例外机制:服务器可以明确告诉浏览器,哪些其他来源的网页可以读取自己的响应。

什么叫"同源"?

一个源由三部分共同组成:

Origin = 协议 + 主机 + 端口

这三者只要有一个不同,就是不同源:

text 复制代码
http://localhost:5173
-> http://localhost:3000   不同端口,跨源

http://localhost:3000
-> https://localhost:3000  不同协议,跨源

http://localhost:3000
-> http://127.0.0.1:3000  不同主机,跨源

跨域不等于请求绝对发不出去

浏览器需要允许很多跨源资源加载,否则网页根本无法使用图片、样式表或跳转链接。

真正受保护的重点是:

所以,看到 CORS 报错时,更准确的理解是:

浏览器不允许当前网页的 JavaScript 读取这次跨源响应。

这句话很重要。CORS 不等于请求一定没有到达服务器。 对于一些请求,浏览器可能会发送请求,但把响应拦在网页 JavaScript 之外。

这也是为什么 CORS 不能当作后端的认证、授权或 CSRF 防护。服务端仍然必须自己验证用户身份、权限和请求是否合法。

CORS 如何表达"同意"?

当网页 A 请求服务器 B 时,浏览器会带上请求来源信息:

http 复制代码
Origin: https://a.example

如果服务器 B 允许 https://a.example 的网页读取响应,应在响应中返回:

http 复制代码
Access-Control-Allow-Origin: https://a.example

Access-Control-Allow-Origin 的含义是:

允许来自 https://a.example 的网页读取这次响应。

其因果链如下:

所以,浏览器不是在信任网页 A,而是在执行服务器 B 对网页 A 做出的授权声明。

但这里的"授权"只针对浏览器能否把响应交给网页 JavaScript,不替代业务层面的登录认证和权限判断。

预检请求

并不是所有跨源请求都需要服务器先允许。

像有些满足 CORS"简单请求"条件的请求,浏览器会直接发送;而有些请求不满足这些条件,浏览器会先发送一个 OPTIONS 请求询问目标服务器。这个"先问"的行为,就叫预检请求(Preflight Request)

这里不是浏览器判断"这个业务风险高不高",而是浏览器根据方法、请求头和 Content-Type 是否属于 CORS safelist 来决定是否预检。

预检通常会询问三件事:

http 复制代码
Origin: https://a.example
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization, content-type

服务器必须分别回应:

  • 是否允许这个来源。
  • 是否允许这个 HTTP 方法。
  • 是否允许这些额外请求头。

例如:

http 复制代码
Access-Control-Allow-Origin: https://a.example
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type

PUTDELETEAuthorization 请求头、application/json 等情况,通常会触发预检。

预检的目的在于:

让目标服务器先声明:浏览器是否可以从这个源,以这个方法、带着这些请求头,发起实际跨源请求并读取响应。

即使预检通过,服务器在真正收到实际请求后,仍然必须完成认证、授权和业务校验。

一个完整的跨域请求

1. 预检请求

浏览器先发送 OPTIONS。如果服务器返回的 CORS 响应头满足要求,浏览器才继续发送实际请求。

2. 实际请求

实际响应也需要包含相应的 CORS 响应头,浏览器才会把响应内容交给网页 JavaScript。

跨域请求想携带 Cookie,还需要什么?

跨源请求是否携带 Cookie,不是只配置 Access-Control-Allow-Origin 就够了。

前端需要显式声明:

js 复制代码
fetch('https://api.example.com/orders', {
  credentials: 'include'
})

服务端则需要返回:

http 复制代码
Access-Control-Allow-Origin: https://a.example
Access-Control-Allow-Credentials: true

命令行或后端服务不受 CORS 影响

如果攻击者不是从浏览器调用,而是使用命令行程序或后端服务,则不会遇到 CORS 报错。

因为 CORS 从一开始就是浏览器的同源策略机制。curl、后端服务不受浏览器规则约束;它们能否访问接口,仍应该由服务端的身份认证、权限授权和网络安全策略决定。

总结

同源策略默认不让网页 JavaScript 读取跨源响应;CORS 是服务器授予特定来源读取权限的声明;它不是服务端安全边界,更不能替代认证、授权和 CSRF 防护。

相关推荐
Lyra_Infra1 小时前
Docker OCI Runtime 启动失败问题排查与解决
后端·docker·架构
XuCoder1 小时前
Redis 哨兵模式:它到底是怎么保证高可用的
后端
敲个大西瓜2 小时前
Springboot核心面试题
java·spring boot·后端
运维行者_3 小时前
网络监控与ITSM集成:从告警到工单,实现运维自动化闭环
开发语言·网络·分布式·后端·架构·flask·php
子兮曰3 小时前
解剖 Claude Code:从入口架构逆向工程看 AI 编程工具的信任边界设计
前端·后端·claude
颜进强4 小时前
前端看后端 15:什么是 DNS?
前端·后端·ai编程
文艺理科生4 小时前
LangChain.js-v1-记忆管理最佳实践
前端·javascript·后端
子兮曰4 小时前
Elysia vs Hono 完整性能对比:Bun 专属优化还是跨平台通用,2026 实测数据给出的答案
前端·后端·typescript
小满zs4 小时前
Go语言第七章(Map)
后端·go