浏览器 Web 安全基础系列

  1. 浏览器怎么判断是不是同一个网站(当前篇)

  2. Cookie 为什么会自动携带

  3. Session 登录状态为什么可能被劫持

  4. HTTPS 为什么仍然不够

  5. CORS 到底在限制什么

  6. CSRF 是怎么冒用登录状态的

  7. CSRF 如何防御

  8. XSS 为什么能在你的网站执行

  9. CSP 与浏览器安全响应头

  10. Clickjacking 点击劫持

  11. 第三方资源为什么危险

  12. 防盗链到底防什么

很多浏览器安全问题都从一句话开始:这个请求来自哪里? 浏览器并不是只看域名,它会使用 Origin、Site 等不同边界处理脚本、存储、Cookie 和网络请求。把“同源”和“同站”混为一谈,后面的 CORS、SameSite、CSRF 都容易理解错。

同源与同站判断

URL 中哪些部分决定 Origin

Origin 可以简单理解为:

协议 + 主机 + 端口

例如:

https://api.example.com:443/users

它的 Origin 是:

https://api.example.com:443

路径 /users 和查询参数不参与 Origin 判断。两个地址只有协议、主机和端口全部相同,才是同源。

地址 A

地址 B

结果

原因

https://example.com/a

https://example.com/b

同源

只有路径不同

http://example.com

https://example.com

跨源

协议不同

https://www.example.com

https://api.example.com

跨源

主机不同

http://localhost:3000

http://localhost:4000

跨源

端口不同

在浏览器控制台可以直接查看:

console.log(location.origin);
// 例如:http://localhost:3000

同源策略到底限制什么

同源策略主要限制一个 Origin 下的脚本读取另一个 Origin 的敏感数据。例如恶意网站不能直接读取你已经登录的邮箱页面 DOM,也不能随便读取另一个站点的 localStorage

但它不是“跨源请求禁止器”。浏览器通常允许:

  • 点击跨源链接;

  • 提交跨源 HTML 表单;

  • 通过 <img> 加载跨源图片;

  • 通过 <script src> 加载跨源脚本;

  • <iframe> 中嵌入允许被嵌入的页面。

限制最严格的是跨源读取。这也是为什么攻击者可能让浏览器把 CSRF 请求发送出去,却无法读取响应。

浏览器跨源行为模型

“站”与“源”不是同一个概念

SameSite Cookie 使用的是“站”边界。现代浏览器通常按下面的核心信息判断站点:

协议 + 可注册域名

端口不参与 SameSite 判断,子域名通常归入同一个站点。因此:

https://www.example.com
https://api.example.com

它们跨源,但通常同站。

本地实验中:

http://localhost:3000
http://localhost:4000

也是跨源但同站。端口不同会触发同源策略和 CORS,但 SameSite=Lax 不一定阻止 Cookie,因为 SameSite 根本不看端口。

而:

http://localhost:3000
http://127.0.0.1:4000

主机不同,通常会被视为跨站。

Public Suffix 为什么存在

如果简单地把最后两段域名当作“站”,a.github.iob.github.io 就会被误认为同站。实际上 github.io 是公共后缀,两个用户站点必须隔离。浏览器使用 Public Suffix List 判断哪个部分是可注册域名。

因此不能只靠字符串切割域名来模拟浏览器的 SameSite 逻辑。生产代码需要精确白名单时,应使用 URL 解析和成熟的公共后缀数据。

Origin 请求头

浏览器在许多 POST、PUT、DELETE 或跨源请求中发送:

Origin: https://www.example.com

它只包含协议、主机和端口,不包含具体路径,适合服务端做请求来源校验。

服务端不能这样判断:

// 错误:example.com.evil.com 也能包含这个字符串
if (req.headers.origin?.includes('example.com')) {
  allow();
}

应该做精确匹配:

const allowedOrigins = new Set([
  'https://www.example.com',
  'https://admin.example.com'
]);

function isAllowedOrigin(req) {
  const origin = req.headers.origin;
  return Boolean(origin && allowedOrigins.has(origin));
}

常见误区

跨域就是跨站

不对。不同端口和不同子域名经常是跨源但同站。

同源策略会阻止请求到达服务器

不一定。跨源表单、图片和部分简单请求可以到达服务器;浏览器主要阻止脚本读取不允许的响应。

后端服务之间也受同源策略限制

同源策略是浏览器安全机制。Node.js、curl、Postman 不会自动执行浏览器的同源策略和 CORS 限制。

小结

后续可以一直用两句话检查问题:

同源:协议、主机、端口是否完全一致?
同站:协议和可注册域名是否属于同一个站点?

同源策略管理脚本和数据隔离,SameSite 管理 Cookie 的跨站发送。二者边界不同,不能互相替代。

参考资料