业务中的跨域策略和 SameSite Cookie

注意,我们整篇文章都在讨论跨域,相同域下的资源访问是没有这些问题的。

前端很多时候,会需要请求跨域的资源(接口或图片等)。

你是资源提供方

你希望限制访问

这时候,你就需要用到跨域资源共享(CORS)了,你可以通过在 Response header 上添加 Access-Control-* 系列字段,明确声明你的资源的访问策略。如:

  • Access-Control-Allow-Origin 限制仅某些域才能访问
  • Access-Control-Allow-Methods 只接受特定方法(GetPost 等)的访问
  • Access-Control-Allow-Credentials 请求要不要携带CredentialsCredentials 可以是 cookiesauthorization headersTLS client certificates
  • 等等

此外,我们知道 Cookie 在网络传输中扮演着很重要的角色,你希望在各种请求中控制 Cookie 是否发送。

此时,虽然 Access-Control-Allow-Credentials 可以控制跨域请求是否允许携带凭据,但它只能在 CORS 请求方面起作用。

如果一个恶意网站用 iframe 嵌套了你的网站,恶意网站有可能会误导用户执行特定的操作。此时用户的操作携带 Cookie 的话,将会很危险。

别担心,有 SameSite Cookie

SameSite Cookie 属性是一种更广泛的机制,可以控制在所有类型的跨站点请求(包括页面导航、图片加载、iframe 加载等)中是否发送 Cookie。

建议看看阮一峰的 Cookie 的 SameSite 属性,介绍得很清楚。

Chrome 默认的 SameSiteLax,是比较严格的,只有一些通常意义上不会造成资源修改的操作才允许携带 Cookie,最大限度地保障了用户的网络安全。

你希望允许访问

我们在业务中经常碰到的问题是,两个域都是我自己的,为什么这个跨站请求没有携带 Cookie 呢?

此时,我们服务端需要在 Response header set-Cookie 字段中,声明 SameSite=None

set-Cookie: xxx=xxx;Domain=.your-target.site;SameSite=None;Secure

这样,*.your-target.site 就能附带 Cookie 访问你的资源了。

你是访问者

通常,服务提供者是不会随便对所有域设置 SameSite=None 的,通常只会对特定的几个域(他自己持有的或信任的域)设置 SameSite=None

假设 third-party.site 是一个很牛哔的站点,它提供了网络基础设施,你今天访问了 A/B/C/D/E 各种网站,都引用了 third-party.site 的资源。

如果浏览器允许随便携带 Cookie,那就糟了,third-party.site 就知道你的访问记录了。这就叫跨站跟踪。

所以,Chrome 默认 SameSite=Lax,很多请求都不允许携带 Cookie,尽可能保护用户的隐私。

当然,你说如果 third-party.site 设置所有域都 SameSite=None,那不就能跟踪了吗?确实,这样是能跟踪了,但是它自身也冒着其他的一些安全风险,就不赘述了。

业务上的常见问题

  1. 我本地开发,SameSite 有问题啊

    本地开发的时候,我们可能需要在其他设备上访问本机起的服务,通常是访问 http://192.168.x.x,这时候就有问题了:

    • SameSite=None 必须要 Secure 才能生效,可是 http://192.168.x.x 不是一个安全的域
    • SameSite=Lax 又会导致一些跨域请求被阻止

    这时候可以再起一个 https 服务,代理到 http 的端口上,其他设备上就能访问 httpsSameSite=None 就能生效了。

    推荐一个方便地起 https 的命令行工具:local-ssl-proxy

  2. 我不是允许跨域了吗?怎么请求还是被阻止了?

    注意看看 Request header 是不是添加了一些自定义的字段,如果有,需要在 Response header Access-Control-Expose-Headers 中添加该字段。

    注意看 devtool -> Networkdevtool -> Console tab 中的报错信息,一般报错信息会详细地告诉你请求为什么被阻止。


P.S. 前段时间写的 《为什么 Chrome 要禁止跨域》 被我不小心误删了,才发现数据库备份脚本权限有问题,一直没有执行。删除也是物理删除,没有做假删除,直接找不回来了。尬了,只能重新写了。。。

如非特别声明,本站作品均为原创,遵循【自由转载-保持署名-非商用-非衍生 创意共享 3.0 许可证】。

对于转载作品,如需二次转载,请遵循原作许可。