前情提要
今天在工作中遇到一个问题,Chrome浏览器请求应用注册在127.0.0.1:xxxxx
服务的接口总是超时;
而且奇怪的是,这里发起的是个GET请求,为什么会有OPTIONS预检呢?看下这个预检的请求。
排查了Web页面和本地应用,对应的模块都没有做改动;
浏览器的请求复制成curl命令后,再在命令行中发起请求也能正常响应。
查自己的代码无果,最后不得不开始怀疑是不是浏览器发生了变化;上网查了一通资料,发现是Chrome的安全策略发生了变化,导致了这个问题:Chrome 安全策略更新-网站访问本地localhost的限制 - 掘金
可以说是天降横锅。
什么样的请求会受到限制?
Address block | Name | Reference | Address space |
---|---|---|---|
127.0.0.0/8 | IPv4 Loopback | RFC1122 | local |
10.0.0.0/8 | Private Use | RFC1918 | private |
172.16.0.0/12 | Private Use | RFC1918 | private |
192.168.0.0/16 | Private Use | RFC1918 | private |
169.254.0.0/16 | Link Local | RFC3927 | private |
::1/128 | IPv6 Loopback | RFC4291 | local |
fc00::/7 | Unique Local | RFC4193 | private |
fe80::/10 | Link-Local Unicast | RFC4291 | private |
::ffff:0:0/96 | IPv4-mapped | RFC4291 | see mapped IPv4 address |
上表摘自WICG对Private Network Access的提案,从外到内的访问会受到限制:
- public访问private
- public访问local
- private访问local
即下图蓝色箭头所示。
发起这样的请求时,Chrome浏览器会先发送预检请求;需要预检成功,才能继续发起请求。
为什么要这么改?
这个改动的动机是保护私有网络中的路由器或其他设备免于CSRF攻击。这里是Chrome的Blog里贴的Security Affairs的文章,介绍了这种攻击的情况。
一个典型的场景是,用户跟存在CSRF攻击的页面发生了互动,让这个页面能够对私有网络中的路由器发起请求;这个请求修改了路由器的DNS服务,导致这个网络中的其他设备在访问银行页面时使用了恶意的DNS记录,导向了钓鱼网站。
什么时候改的?
需要注意的是,中文网络下查到的Chrome浏览器引入变更的时间表,跟我在Chrome团队的Blog看到的时间表都不同。建议以Chrome的博客为准。下面引用自Rollout plan - Private Network Access: introducing preflights。
Rollout plan
Chrome will roll this change out in two phases to give websites time to notice the change and adjust accordingly.
- In Chrome 98:
- Chrome sends preflight requests ahead of private network subresource requests.
- Preflight failures only display warnings in DevTools, without otherwise affecting the private network requests.
- Chrome gathers compatibility data and reaches out to the largest affected websites.
- We expect this to be broadly compatible with existing websites.
- In Chrome 101 at the earliest:
- This will begin only if and when compatibility data indicates that the change is safe enough and we’ve outreached directly when necessary.
- Chrome enforces that preflight requests must succeed, otherwise failing the requests.
- A deprecation trial starts at the same time to allow for websites affected by this phase to request a time extension. The trial will last for at least 6 months.
简单翻译一下就是:
Chrome会分两个阶段来推出这个私有网络访问(Private Network Access,后面简写成PNA)的限制;
- Chrome 98 开始会在PNA请求之前发送预检请求,预检失败会在开发者工具中展示一个警告,不影响私有网络请求。Chrome会收集兼容性数据并联系受影响最严重的网站。我们希望这个可以和大部分现有网站兼容。
- 最早在Chrome 101版本(需要兼容性数据显示更改足够安全才会做出更改),需要预检成功才能发送私有网络请求,否则请求会失败。网站如果需要更多时间来做迁移或变更,可以找谷歌申请。
如何解决?——使用预检
预检请求是跨域资源共享 (CORS) 标准引入的一种机制,用于在向目标网站发送可能有副作用的 HTTP 请求之前向其请求许可。这确保了目标服务器理解 CORS 协议并显着降低了 CSRF 攻击的风险。
预检请求引入了一对新的请求头和响应头:
Access-Control-Request-Private-Network: true
这个请求头会出现在PNA预检请求的Header中Access-Control-Allow-Private-Network: true
这个响应头用于PNA预检请求的响应,表示服务接受PNA请求。
无论请求方法和模式如何,都会为所有专用网络请求发送 PNA 的预检请求。它们在 cors 模式以及 no-cors 和所有其他模式下的请求之前发送。这是因为所有私网请求都可以用于 CSRF 攻击,无论请求模式如何,以及响应内容是否对发起者可用。
简单来说,你需要在原来的本地服务端实现OPTIONS预检请求的处理,并在响应中加上Access-Control-Allow-Private-Network: true
的响应头,这样后面的请求就能正常进行了。