写一些不能单独成文或者不好分类的文字。
2024-09
[09-12] 关于 iframe 的同源策略限制和 Content-Security-Policy
今天遇到一个用户提的 oncall,用户在我们的系统底下嵌入了一个 iframe,iframe 内容是用户公司自建的页面。用户的诉求是从子页面拿到顶层页面 (iframe 宿主) 的 URL,然后遇到了跨域问题,用户提问是否可以配置 iframe 属性或者配置 CSP 绕过,把我问住了。
我们来尝试复现一下用户的场景,用户是这样做的:
http://localhost:8080
<html>
<head>
<title>Parent Page</title>
</head>
<body>
<iframe src="http://localhost:8081/" />
</body>
</html>
http://localhost:8081
<html>
<head>
<title>Child Page</title>
</head>
<body>
<script>
console.log(window.top.location.href);
</script>
</body>
</html>
不出意外就要出意外地报错了:
Uncaught SecurityError: Failed to read a named property 'href' from 'Location': Blocked a frame with origin "http://localhost:8081" from accessing a cross-origin frame.
很明显问题是出在了 window.top.location.href
的读取(跨源访问)被浏览器的同源安全策略拦截了。根据 MDN 上对 浏览器同源策略 的描述,大部分属性的跨源访问是只读的,唯独对 Location
对象的 href
属性是只写的(实际上大部分的 “跨源读操作” 一般是不被允许的):
同样地从父页面访问子页面的 window 和 Location 对象也是受限的,这种情况下设置 Access-Control-Allow-Origin
也不好使。一般来说这种情况最好的办法是直接通过 iframe src 设置 URL query 来传递父页面 URL,或者需要传递复杂信息的时候使用 window.postMessage
.
问题其实到这里解决了,但是用户一开始提出的方案 “配置 iframe 属性或者配置 CSP 绕过” 是否有可行性呢。
iframe sandbox
先说说是否能配置 iframe 的属性来绕过一些浏览器的安全限制。iframe 有一个 sandbox
属性,允许开发者限制 iframe 中页面的行为,如执行 JS、下载文件、提交表单、弹窗等,也可以强制浏览器将一个同源的 iframe 页面视为非同源页面。
但实际上我们直接使用 <iframe src="xxx" />
这样来嵌入一个 iframe 的时候,不会应用任何限制,所以其实没有必要特地指定属性,对 iframe 和浏览器来说同源就是同源,非同源就是非同源,指定 sandbox
的 allow-scripts
或 allow-same-origin
限制并不能改变这一点。
Content Security Policy
再说说内容安全策略 (Content Security Policy, CSP)。 其实今天之前我也不知道 CSP 具体有啥用,所以用户提到这个的时候我也有些懵逼。
简而言之,CSP 主要是控制浏览器可以为该页面获取哪些资源,一般我们可以在返回的响应头或者 meta 中设置 CSP:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src https://*; child-src 'none';" />
CSP 通过特定的 DSL 来描述访问限制的策略,格式是 key-src domain1 [domain2] ... ;
,更多示例可以看 MDN.
- 必须包含一个
default-src
default-src 'self'
表示当前页面引用的所有内容,都只能来自当前页面所在的域名(子域名也不行)- 可以使用通配符,指定一个白名单,信任除当前页面域名外的某个域名及其子域名:
default-src 'self' *.trusted.com
- 通过
img-src
限制图片引用、media-src
限制媒体文件引用、script-src
限制脚本引用:default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com
- 利用 CSP 也可以用来做其它的限制,例如强制使用来自 SSL 的资源:
default-src https://xxx.com
CSP 还可以设置为警告模式,并且还能够通过 report-uri
来将违反 CSP 的行为报告给服务器。
总之 CSP 是个比较冷门的功能,可以用 CSP 来减少 XSS 攻击的发生。同样,默认不设置 CSP 的时候,其实也是采取了最宽松的引用限制的。所以它能解决用户的问题吗?答案也是不能。