跨域知识点整理
最近工作中总出现跨域问题,而且存在一些意识模糊的地方。或者被搞模糊的地方,于是重新完整梳理一下疑惑的地方。
什么是跨域?
浏览器的同源策略
- 协议相同:
http://
,https://
,ftp://
- 域名相同:
a.com
,b.com
,b.a.com
- 端口相同:
https://a.com
,https://a.com:81
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路径不同 |
https://store.company.com/secure.html |
失败 | 协议不同 |
http://store.company.com:81/dir/etc.html |
失败 | 端口不同 ( http:// 默认端口是 80) |
http://news.company.com/dir/other.html |
失败 | 主机不同 |
跨源网络访问
同源策略控制不同源之间的交互,例如在使用XMLHttpRequest
或 img
标签时则会受到同源策略的约束。这些交互通常分为三类:
- 跨域写操作(Cross-origin writes)一般是被允许的。例如链接(links),重定向以及表单提交。特定少数的 HTTP 请求需要添加 preflight。
- 跨域资源嵌入(Cross-origin embedding)一般是被允许(后面会举例说明)。
<script src="..."></script>
标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。<link rel="stylesheet" href="...">
标签嵌入 CSS。由于 CSS 的松散的语法规则,CSS 的跨域需要一个设置正确的 HTTP 头部Content-Type
。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至 CVE-2010-0051)部分 和 Opera。- 通过
img
展示的图片。支持的图片格式包括 PNG,JPEG,GIF,BMP,SVG,... - 通过
video
和audio
播放的多媒体资源。 - 通过
<object>
、embed
和<applet>
嵌入的插件。 - 通过
@font-face
引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。 - 通过
<iframe>
载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互。
- 跨域读操作(Cross-origin reads)一般是不被允许的,但常可以通过内嵌资源来巧妙的进行读取访问。例如,你可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或availability of an embedded resource.
Get 全部都不跨域?
Get 是跨域的,只是跨域资源嵌入(Cross-origin embedding)一般是被允许的。通俗说法是通过 HTML 内嵌标签绕过限制是被允许的。
获取图片没有跨域?
因为图片和 new Image()
都是通过内嵌标签绕过 GET 的跨域限制。
Q: 为什么 new Image()
可以?
A: 因为调用 new Image()
返回的是一个 <img>
元素标签。它本质上是一个 img 元素标签。
var i = new Image() // <img>
什么情况下需要支持跨域?
可以使用 CORS 来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。
跨域资源共享标准( cross-origin sharing standard )允许在下列场景中使用跨域 HTTP 请求:
- 前文提到的由
XMLHttpRequest
或 Fetch 发起的跨域 HTTP 请求。 - Web 字体 (CSS 中通过
@font-face
使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。 - WebGL 贴图
- 使用
drawImage
将 Images/video 画面绘制到 canvas,这一点我们使用 html2canvas 这个库的时候确确实实遇到了这个问题。
安全性和“被污染”的 canvas
由于在 <canvas>
位图中的像素可能来自多种来源,包括从其他主机检索的图像或视频,因此不可避免的会出现安全问题。
尽管不通过 CORS 就可以在 <canvas>
中使用其他来源的图片,但是这会污染画布,并且不再认为是安全的画布,这将可能在 <canvas>
检索数据过程中引发异常。
如果从外部引入的 HTML <img>
或 SVG <svg>
,并且图像源不符合规则,将会被阻止从 <canvas>
中读取数据。
在"被污染"的画布中调用以下方法将会抛出安全错误:
- 在
<canvas>
的上下文上调用getImageData()
- 在
<canvas>
上调用toBlob()
- 在
<canvas>
上调用toDataURL()
这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。
如何阻止跨源访问
- 阻止跨域写操作,只要检测请求中的一个不可推测的标记(CSRF token)即可,这个标记被称为 Cross-Site Request Forgery (CSRF) 标记。你必须使用这个标记来阻止页面的跨站读操作。
- 阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。
- 阻止跨站嵌入,需要确保你的资源不能通过以上列出的可嵌入资源格式使用。浏览器可能不会遵守
Content-Type
头部定义的类型。例如,如果您在 HTML 文档中指定<script>
标记,则浏览器将尝试将标签内部的 HTML 解析为 JavaScript。 当您的资源不是您网站的入口点时,您还可以使用 CSRF 令牌来防止嵌入。