XSS 跨站脚本
- 可以窃取 Cookie 信息。恶意 JavaScript 可以通过“document.cookie”获取 Cookie 信息,然后通过 XMLHttpRequest 或者 Fetch 加上 CORS 功能将数据发送给恶意服务器;恶意服务器拿到用户的 Cookie 信息之后,就可以在其他电脑上模拟用户的登录,然后进行转账等操作。
- 可以监听用户行为。恶意 JavaScript 可以使用“addEventListener”接口来监听键盘事件,比如可以获取用户输入的信用卡等信息,将其发送到恶意服务器。黑客掌握了这些信息之后,又可以做很多违法的事情。
- 可以通过修改 DOM 伪造假的登录窗口,用来欺骗用户输入用户名和密码等信息。
- 还可以在页面内生成浮窗广告,这些广告会严重地影响用户体验。
存储型 XSS 攻击
- 首先黑客利用站点漏洞将一段恶意 JavaScript 代码提交到网站的数据库中;
- 然后用户向网站请求包含了恶意 JavaScript 脚本的页面;
- 当用户浏览该页面的时候,恶意脚本就会将用户的 Cookie 信息等数据上传到服务器。
反射型 XSS 攻击
用户将一段含有恶意代码的请求提交给 Web 服务器,Web 服务器接收到请求时,又将恶意代码反射给了浏览器端
比如通过邮件,扫码,等传播恶意链接
基于 DOM 的 XSS 攻击
这种劫持类型很多,有通过 WiFi 路由器劫持的,有通过本地恶意软件来劫持的,它们的共同点是在 Web 资源传输过程或者在用户使用页面的过程中修改 Web 页面的数据。
防御方法:
-
服务器对输入脚本进行过滤或转码(转码指类似于
<script>转成<script>) -
- 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
- 禁止向第三方域提交数据,这样用户数据也不会外泄;
- 禁止执行内联脚本和未授权的脚本;
- 还提供了上报机制,这样可以帮助我们尽快发现有哪些 XSS 攻击,以便尽快修复问题。
-
HttpOnly 属性:这样恶意脚本就获取不到cookie了
CSRF 跨站请求伪造
条件
- 第一个,目标站点一定要有 CSRF 漏洞;
- 第二个,用户要登录过目标站点,并且在浏览器上保持有该站点的登录状态;
- 第三个,需要用户打开一个第三方站点,可以是黑客的站点,也可以是一些论坛。
类型
- 自动发起 Get 请求(比如在黑客自己的站点里面嵌入了一个img标签,就会根据src自动请求)
- 自动发起 POST 请求(比如黑客站点里面嵌入了一个表单,form表单不会出现跨域问题)
- 引诱用户点击链接(a标签跳转自动发的get请求)
防御方法:
对于前端:
-
Cookie 的 SameSite 属性:SameSite 选项通常有 Strict、Lax 和 None 三个值。
- Strict 最为严格。如果 SameSite 的值是 Strict,那么浏览器会完全禁止第三方 Cookie。
- Lax 相对宽松一点。在跨站点的情况下,从第三方站点的链接打开和从第三方站点提交 Get 方式的表单这两种方式都会携带 Cookie。但如果在第三方站点中使用 Post 方法,或者通过 img、iframe 等标签加载的 URL,这些场景都不会携带 Cookie。
- None 的话,在任何情况下都会发送 Cookie 数据。
对于后端:
- 验证请求的来源站点(Referer 和 Origin 属性)(Origin 属性只包含了域名信息)
- CSRF Token
安全沙箱
安全沙箱最小的保护单位是进程,并且能限制进程对操作系统资源的访问和修改,这就意味着如果要让安全沙箱应用在某个进程上,那么这个进程必须没有读写操作系统的功能,比如读写本地文件、发起网络请求、调用 GPU 接口等。
站点隔离? 所谓站点隔离是指 Chrome 将同一站点(包含了相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行。
- 以前是标签级隔离,但是iframe可能不安全
- 现在是iframe级别隔离
安全沙箱如何影响各个模块功能?
先说总结,全靠浏览器内核
1. 持久存储
我们先来看看安全沙箱是如何影响到浏览器持久存储的。由于安全沙箱需要负责确保渲染进程无法直接访问用户的文件系统,但是在渲染进程内部有访问 Cookie 的需求、有上传文件的需求,为了解决这些文件的访问需求,所以现代浏览器将读写文件的操作全部放在了浏览器内核中实现,然后通过 IPC(跨进程通信) 将操作结果转发给渲染进程。
具体地讲,如下文件内容的读写都是在浏览器内核中完成的:
存储 Cookie 数据的读写。通常浏览器内核会维护一个存放所有 Cookie 的 Cookie 数据库,然后当渲染进程通过 JavaScript 来读取 Cookie 时,渲染进程会通过 IPC 将读取 Cookie 的信息发送给浏览器内核,浏览器内核读取 Cookie 之后再将内容返回给渲染进程。
一些缓存文件的读写也是由浏览器内核实现的,比如网络文件缓存的读取。
2. 网络访问
同样有了安全沙箱的保护,在渲染进程内部也是不能直接访问网络的,如果要访问网络,则需要通过浏览器内核。不过浏览器内核在处理 URL 请求之前,会检查渲染进程是否有权限请求该 URL,比如检查 XMLHttpRequest 或者 Fetch 是否是跨站点请求,或者检测 HTTPS 的站点中是否包含了 HTTP 的请求。
3. 用户交互
渲染进程实现了安全沙箱,还影响到了一个非常重要的用户交互功能。
通常情况下,如果你要实现一个 UI 程序,操作系统会提供一个界面给你,该界面允许应用程序与用户交互,允许应用程序在该界面上进行绘制,比如 Windows 提供的是 HWND,Linux 提供的 X Window,我们就把 HWND 和 X Window 统称为窗口句柄。应用程序可以在窗口句柄上进行绘制和接收键盘鼠标消息。
不过在现代浏览器中,由于每个渲染进程都有安全沙箱的保护,所以在渲染进程内部是无法直接操作窗口句柄的,这也是为了限制渲染进程监控到用户的输入事件。
由于渲染进程不能直接访问窗口句柄,所以渲染进程需要完成以下两点大的改变。
第一点, 渲染进程需要渲染出位图,渲染进程需要将生成好的位图发送到浏览器内核,然后浏览器内核将位图复制到屏幕上。
第二点, 操作系统没有将用户输入事件直接传递给渲染进程,而是将这些事件传递给浏览器内核。然后浏览器内核再根据当前浏览器界面的状态来判断如何调度这些事件,如果当前焦点位于浏览器地址栏中,则输入事件会在浏览器内核内部处理;如果当前焦点在页面的区域内,则浏览器内核会将输入事件转发给渲染进程。
同源策略
如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。
- DOM层面:同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作
- 数据层面:同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。由于同源策略,我们依然无法通过第二个页面的 opener 来访问第一个页面中的 Cookie、IndexDB 或者 LocalStorage 等内容。
- 网络层面:同源策略限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。
cookie 属性
- Domain:Domain决定Cookie在哪个域是有效的,也就是决定在向该域发送请求时是否携带此Cookie,Domain的设置是对子域生效的
- Path:Path是Cookie的有效路径,和Domain类似,也对子路径生效(如Cookie1和Cookie2的Domain均为a.com,但Path不同,Cookie1的Path为 /b/,而Cookie的Path为 /b/c/,则在a.com/b页面时只可以访问Cookie1,在a.com/b/c页面时,可访问Cookie1和Cookie2。Path属性需要使用符号“/”结尾。
- Secure:Secure为Cookie的安全属性,若设置为true,则浏览器只会在HTTPS和SSL等安全协议中传输此Cookie,不会在不安全的HTTP协议中传输此Cookie。
- SameSite:SameSite用来限制第三方 Cookie,从而减少安全风险。
- Priority:优先级,chrome的提案,定义了三种优先级,Low/Medium/High,当cookie数量超出时,低优先级的cookie会被优先清除。
- partition key:分区键是浏览器在请求设置cookie的端点时访问的顶级URL的站点(方案和可注册域)。cookie分区键的作用是允许开发人员将cookie选择性地放入分区存储中,每个顶级站点都有一个单独的cookie存储区。分区的第三方cookie与其最初设置的顶级站点绑定,无法从其他地方访问。这允许阻止跨站点跟踪,同时仍然可以启用第三方cookie的非跟踪用途,例如在不同站点之间持久化嵌入式地图或聊天小部件的状态,并为子资源CDN负载平衡和无头CMS提供程序持久化配置信息。
如果满足下面几个条件,请求才会自动携带cookie:
- 浏览器端某个 Cookie 的 domain(.a.com) 字段等于请求的域名或者是请求的父域名,请求的域名需要是 a.com/b.a.com 才可以
- 都是 http 或者 https,或者不同的情况下 Secure 属性为 false(即 secure 是 true 的情况下,只有 https 请求才能携带这个 cookie)
- 要发送请求的路径,跟浏览器端 Cookie 的 path 属性必须一致,或者是浏览器端 Cookie 的 path 的子目录,比如浏览器端 Cookie 的 path 为 /test,那么请求的路径必须为/test 或者/test/xxxx 等子目录才可以