3、浏览器安全
3.1、同源策略
什么是同源?
如果两个url的协议、域名、端口相同,就称这两个url是同源
比如http://store.company.com:80/index.html和 http://store.company.com:80/dir/index.html是同源,而 https://store.company.com:80/index.html和 http://store.company.com:80/index.html不同源,因为协议不同。
同源策略主要表现在DOM、Web数据和Web网络三个层面
-
DOM
同源策略限制了来自不同源的js脚本对当前的DOM对象的读和写操作
-
Web数据
同源策略限制了不同源的站点读取当前站点的Cookie、indexDB、LocalStorage等数据。
-
Web网络
在同源策略下不能通过XMLHttpRequest或Fetch进行跨域请求
不过安全性和便利性是相对的,如果让不的同源之间完全隔离那无疑是最安全的,但是这会造成Web难以开发和使用,所以我们需要在安全性和便利性之间做出权衡,让出一些安全性去实现便利性,而安全性的让出就会带来一些安全性的问题,最典型的就是XSS攻击和CSRF攻击。下面来讲讲实现了哪些便利性。
-
不同源之间DOM的操作
在同源策略下如果两个页面是不同源得到,那么是无法相互操纵DOM的,但是在现实情况下经常需要在两个不同源的DOM之间进行通信,于是浏览器引入了跨文档消息机制,可以通过window.postMessage的js接口来和不同源的DOM进行通信。
-
实现跨域资源共享
在同源策略下,如果通过XMLHttpRequest或者Fetch进行跨域请求,同源策略会阻止请求的发送,这样会大大制约我们的生产力,所以浏览器引入了跨域资源共享(CORS),即可以通过该机制进行跨域访问。
-
页面可以引入第三方资源
在同源策略下一个页面的所有资源必须来源于同一个源下,就是要将页面所有的HTML文件、就是文件、图片、视频等资源放到同一台服务器下,这样无疑违背了Web的初衷,所以我们要在同源策略下开一个口子,让页面可以引入第三方的文件,但正如上面我们说的,这也会带来许多安全性的问题,页面的内容可能被一些恶意程序劫持,最常用的劫持方式就是通过插入恶意脚本,当HTML文件被送到浏览器时,浏览器不知道该脚本是不是恶意脚本。所以为了解决这个问题,浏览器引入了内容安全策略CSP,CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。通过这些手段就可以大大减少 XSS 攻击。
3.2、XSS攻击
XSS攻击指的是黑客往HTML文件或者DOM中注入恶意脚本,从而在用户浏览网页的时候通过注入的恶意脚本对用户实施攻击的一种手段。那为什么可以往页面注入恶意脚本病并产生攻击效果呢,因为浏览器无法识别这些脚本是呗恶意注入的还是正常的,所以恶意注入的脚本也有所有脚本的权限,下面看下恶意注入的脚本可以干什么事。
-
获取用户的Cookie信息
恶意注入的js脚本可以通过"document.cookie"获取Cookie信息,然后通过XMLHttpRequest或者Fetch加上CORS机制发送给恶意服务器,恶意服务器拿到Cookie后可以在电脑上模拟登录。
-
监听用户行为
恶意js可以使用"addEventListener"接口来监听键盘事件,可以获取用户输入的信息。
通过修改DOM伪造假的登录窗口,用来欺骗用户输入的用户名和密码等信息
在页面内生成浮窗广告
恶意脚本注入的方式
:
-
存储型XSS攻击
存储型XSS攻击就是黑客通过服务器漏洞向数据库中注入恶意脚本,当用户请求到包含恶意脚本的页面时,用户的Cookie信息等数据就会被获取。 如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中 。
-
反射型XSS攻击
在反射型XSS攻击中,恶意脚本属于用户发送给网站请求中的一部分,当用户将含有恶意脚本的请求提交给服务器时,服务器又会将这部分恶意脚本返回给浏览器,然后就会在用户的页面执行获取用户信息。这需要欺骗用户自己去点击链接才能触发XSS代码 ,一般容易出现在搜索页面 。
反射型XSS攻击与存储型XSS攻击不一样的地方是反射型XSS攻击不会将恶意脚本存储到服务器
-
基于DOM的XSS攻击
基于 DOM 的 XSS 攻击是不牵涉到页面 Web 服务器的。具体来讲,黑客通过各种手段将恶意脚本注入用户的页面中,比如通过网络劫持在页面传输过程中修改 HTML 页面的内容。
如何预防XSS攻击
:
无论是何种XSS攻击,首先都需要在浏览器中注入恶意脚本,然后再通过脚本获取用户信息,那下面来介绍一下常用的策略。
- 服务器对输入的脚本进行过滤或者转码
不管是反射型还是存储型 XSS 攻击,我们都可以在服务器端将一些关键的字符进行过滤,比如
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="javascript" cid="n605" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--font-monospace); font-size: 0.85rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248) !important; position: relative !important; width: inherit; border: 1px solid rgb(244, 244, 244); -webkit-font-smoothing: initial; line-height: 1.43rem; border-radius: 2px; overflow-wrap: normal; margin: 0.8rem 0px !important; padding: 0.3rem 0px !important; color: rgb(52, 73, 94); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">code:<script>alert('你被xss攻击了')</script></pre>
过滤之后就是"code".这样用户在访问页面时,由于脚本被过滤了,所以这些脚本不可能在客户端执行。
除了过滤服务器还可以对这些内容进行转码,上面的代码进行转码之后如下。
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="javascript" cid="n612" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--font-monospace); font-size: 0.85rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248) !important; position: relative !important; width: inherit; border: 1px solid rgb(244, 244, 244); -webkit-font-smoothing: initial; line-height: 1.43rem; border-radius: 2px; overflow-wrap: normal; margin: 0.8rem 0px !important; padding: 0.3rem 0px !important; color: rgb(52, 73, 94); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">code:<script>alert('你被xss攻击了')</script></pre>
即使这段代码被返回给页面也不会执行。
-
充分利用CSP
实施严格的CSP可以有效地防范XSS攻击,CSP功能如下
1、限制加载其他域下的文件,这样即使黑客插入的一个js文件,这个文件也是不会被加载的。
2、禁止第三方域提交数据
3、禁止执行内联脚本和未授权脚本
4、提供上报机制,这样可以更快发现XSS攻击
-
使用HttpOnly
由于很多XSS攻击都是用来盗用用户Cookie的,所以可以通过设置HttpOnly来保护我们的Cookie,设置HttpOnly的Cookie只能使用在Http请求的过程中,无法通过js来获取Cookie。
3.3、CSRF攻击
CSRF(跨站请求伪造)是指黑客诱导用户打开黑客网站,在黑客网站中利用用登录的状态发送跨站请求。和XSS不同的是,CSRF不需要通过在页面注入恶意脚本的形式去获取用户信息,CSRF仅仅是利用用户登录的状态和服务器漏洞来实施攻击
如何预防SDRF攻击
:
首先看下CSRF攻击的条件:
- 服务器必须存在漏洞
- 用户必须在浏览器上保持登录状态
- 用户必须打开第三方站点
与XSS不同的是,CSRF攻击不会在用户页面上注入恶意脚本,所以黑客无法获取用户的Cookie等信息,最关键的一点就是要找到服务器的漏洞,所以对于CSRF攻击来说我们主要的防护手段就是提升服务器的安全性
-
充分利用好Cookie的SameSite属性
CSRF攻击是利用用户的登录状态来发起的攻击,而Cookie正是浏览器和服务器质检维护登录状态的一个关键数据,通常CSRF攻击都是从第三方站点发起的,要防止CSRF攻击,最好的就是在第三方站点发送请求时禁止携带Cookie,而SameSite正好能解决我们的问题,,通过使用SaneSite属性来降低CSRF攻击的风险。
SameSite的属性:
- Strict
Strict是最严格的属性,如果给Cookie设置了Strict,那么浏览器会完全禁止第三方Cookie,就比如通过第三方站点去访问当前站点的资源,那么Cookie是不会被发送到当前站点的服务器上。
- Lax
Lax相对宽松一点,在跨站点的情况下,从第三方站点的链接打开和从第三方站点提交Get的表单都会携带cookie.但是如果在第三方站点中使用Post方法或者通过img、iframe等标签加载的URL,都不会携带Cookie。
- None, 任何情况下都会发送Cookie。
所以我们可以针对一些情况使用Strict或者Lax来防止CSRF攻击
-
验证请求的来源站点
由于 CSRF 攻击大多来自于第三方站点,因此服务器可以禁止来自第三方站点的请求。那么该怎么判断请求是否来自第三方站点呢? 下面介绍下Referer和Origin属性。
-
Referer属性
记录了该http请求的来源地址,但有些场景不适合将来源URL暴露给服务器,所以可以设置不用上传,并且referer属性是可以修改的,所以在服务器端校验referer属性并没有那么可靠
-
origin属性
通过XMLHttpRequest、Fetch发起的跨站请求或者Post方法发送请求时,都会带上origin,所以服务器可以优先判断Origin属性,再根据实际情况判断是否使用referer判断。
因此,服务器的策略是优先判断 Origin,如果请求头中没有包含 Origin 属性,再根据实际情况判断是否使用 Referer 值。
-
-
CSRF Token
使用CSRFToken有两步
在浏览器向服务器发起请求时,服务器生成一个CSRF Token(字符串)发送给浏览器,然后将该字符串放入页面中
浏览器请求时(如表单提交)需要带上这个CSRF Token。服务器收到请求后,验证CSRF是否合法,如果不合法拒绝即可。
其实 CSRF TOKEN类似于cookie, 都是存储用户信息,而此用户信息只存储在当前你请求的站点,而不是浏览器,所以不同的标签页面,或不同次的请求(在浏览器上同时打开两个相同的页面也会生成两个不同的Token)是不共享此用户信息的,所以规避了cookie所带来的的漏洞 。