前端安全
XSS(跨站脚本攻击)
Cross-Site Scripting
(跨站脚本攻击)简称 XSS
,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie
、SessionID
等,进而危害数据安全。
XSS分类
反射型XSS攻击
顾名思义,恶意 JavaScript
脚本属于用户发送给网站请求中的一部分,随后网站又将这部分返回给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中,服务端逻辑会改变最终的网页代码。
反射型 XSS 的攻击步骤:
- 攻击者构造出特殊的
url
,其中包含恶意代码; - 用户打开带有恶意代码的
url
时,网站服务端将恶意代码从url
中取出,拼接在HTML
中返回给浏览器 - 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
基于DOM的XSS攻击
目前更流行前后端分离的项目,反射型 XSS
无用武之地。 但基于DOM
的XSS
攻击不需要经过服务器,我们知道,网页本身的 JavaScript
也是可以改变 HTML
的,黑客正是利用这一点来实现插入恶意脚本。
基于DOM 的 XSS 攻击步骤:
- 攻击者构造出特殊的
url
,其中包含恶意代码 - 用户打开带有恶意代码的
url
- 用户浏览器接收到响应后解析执行,前端
JavaScript
取出url
中的恶意代码并执行 - 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
存储型XSS攻击
又叫持久型 XSS
,顾名思义,黑客将恶意 JavaScript
脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。常见于搜索、微博、社区贴吧评论等
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在
HTML
中返回给浏览器 - 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
几种XSS
攻击类型的区别
- 基于
DOM的XSS
攻击中,取出和执行恶意代码由浏览器端完成,属于前端JavaScript
自身的安全漏洞,其他两种XSS
都属于服务端的安全漏洞 反射型的 XSS
的恶意脚本存在url
里,攻击常见于通过url
传递参数的功能,如网站搜索、跳转等存储型 XSS
的恶意代码存在数据库里,攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等
XSS防范措施
由上面对XSS攻击的介绍我们知道,XSS攻击主要有两大步骤:
- 攻击者提交恶意代码
- 浏览器执行恶意代码
所以我们可以针对这两点来制定防范措施:
输入过滤
在用户提交时,由前端过滤输入,然后提交到后端,这种方法不可行,因为攻击者可能绕过前端过滤,直接构造请求,提交恶意代码。一般在写入数据库前,后端对输入数据进行过滤。虽然输入侧过滤能够在某些情况下解决特定的 XSS
问题,但会引入很大的不确定性和乱码问题。在防范 XSS
攻击时应避免此类方法。
预防存储型和反射型 XSS 攻击
- 改成纯前端渲染,把代码和数据分隔开
- 对 输出的
HTML
做充分转义
预防 DOM型XSS攻击
DOM型XSS攻击
,实际上就是网站前端 JavaScript
代码本身不够严谨,把不可信的数据当作代码执行了。
在使用 .innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML
插到页面上,而应尽量使用 .textContent
、.setAttribute()
等。
如果用 Vue/React
技术栈,并且不使用 v-html
/dangerouslySetInnerHTML
功能,就在前端 render
阶段避免 innerHTML
、outerHTML
的 XSS
隐患。
DOM 中的内联事件监听器,如 location
、onclick
、onerror
、onload
、onmouseover
等,<a>
标签的 href
属性,JavaScript
的 eval()
、setTimeout()
、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API
,很容易产生安全隐患,请务必避免。
CSP(Content Security Policy,内容安全策略),定义域白名单
严格的 CSP
在 XSS
的防范中可以起到以下的作用:
- 禁止加载外域代码,防止复杂的攻击逻辑
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域
- 禁止内联脚本执行(规则较严格,目前发现
GitHub
使用) - 禁止未授权的脚本执行(新特性,
Google Map
移动版在使用) - 合理使用上报可以及时发现
XSS
,利于尽快修复问题
设置 Cookie
的 HttpOnly
属性,禁止JavaScript
读取cookie
验证码:防止脚本冒充用户提交危险操作
CSRF(跨站请求伪造)
CSRF(Cross-site request forgery)
:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
CSRF攻击步骤
典型的CSRF攻击是这样的:
- 受害者登录
A
网站,并且保留了登录凭证(Cookie
) - 攻击者引诱受害者访问
B
网站 B
网站向A
网站发送了一个请求(这个就是下面将介绍的几种伪造请求的方式),浏览器请求头中会默认携带A
网站的Cookie
A
网站服务器收到请求后,经过验证发现用户是登录了的,所以会处理请求
常见的CSRF攻击类型
GET类型的CSRF
GET类型的CSRF利用非常简单,只需要一个HTTP请求,一般会这样利用:
<img src="http://bank.example/withdraw?amount=10000&for=hacker" >
在受害者访问含有这个img
的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker
发出一次HTTP
请求。bank.example
就会收到包含受害者登录信息的一次跨域请求
POST类型的CSRF
这种类型的CSRF
利用起来通常使用的是一个自动提交的表单,如:
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST
操作。
POST
类型的攻击通常比GET
要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。
链接类型的CSRF
链接类型的CSRF
并不常见,比起其他两种用户打开页面就中招的情况,这种需要用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
重磅消息!!
<a/>
CSRF的特点
- 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是"冒用"
- 跨站请求可以用各种方式:
图片url
、超链接
、CORS
、Form提交
等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪
CSRF防范措施
由上面对CSRF
的介绍我们知道了,CSRF
通常发生在第三方域名,并且CSRF
攻击者不能获取到受害者的cookie
等信息,只是借用他们的登录状态来伪造请求。所以我们可以针对这两点来制定防范措施:
同源检测
既然CSRF
大多来自第三方网站,那么我们就直接禁止第三方域名(或者不受信任的域名)对我们发起请求。
在HTTP
协议中,每一个异步请求都会携带两个Header
,用于标记来源域名:
- Origin Header
- Referer Header
这两个Header
在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。 服务器可以通过解析这两个Header
中的域名,确定请求的来源域。同时服务器应该优先检测 Origin
。为了安全考虑,相比于 Referer
,Origin
只包含了域名而不带路径。
CSRF Token
- 在浏览器向服务器发起请求时,服务器生成一个
CSRF Token
。CSRF Token
其实就是服务器生成的随机字符串,然后将该字符串植入到返回的页面中,通常是放到表单的隐藏输入框中,这样能够很好的保护CSRF Token
不被泄漏; - 当浏览器再次发送请求的时候,就需要携带这个
CSRF Token
值一起提交 - 服务器验证
CSRF Token
是否一致;从第三方网站发出的请求是无法获取用户页面中的CSRF Token
值的
给 Cookie 设置合适的 SameSite
当从 A
网站登录后,会从响应头中返回服务器设置的 Cookie
信息,而如果 Cookie
携带了 SameSite=strict
则表示完全禁用第三方站点请求头携带 Cookie
,比如当从 B 网站请求 A 网站接口的时候,浏览器的请求头将不会携带该 Cookie
。
Samesite
取值
Samesite=Strict
,这种称为严格模式,表明这个Cookie
在任何情况下都不可能作为第三方Cookie
Samesite=Lax
,这种称为宽松模式,比Strict
放宽了点限制:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET
请求,则这个Cookie
可以作为第三方Cookie
。(默认)None
任何情况下都会携带
ClickJacking(点击劫持)
Clickjacking(点击劫持)
是一种视觉上的欺骗手段,攻击者通过使用一个透明的iframe
(一般是通过设置opacity
的方式),覆盖在一个网页上,然后诱使用户在该页面上进行操作,通过调整iframe
页面的位置,可以使得伪造的页面恰好和iframe
里受害页面里一些功能重合(按钮),以达到窃取用户信息或者劫持用户操作的目的。
Clickjacking
是仅此于XSS
和CSRF
的前端漏洞,因为需要诱使用户交互,攻击成本高,所以不被重视,但危害不容小觑。
防范措施
- 在
HTTP
中加入X-FRAME-OPTIONS
属性,此属性控制页面是否可被嵌入iframe
中 - 判断当前网页是否被 iframe 嵌套
X-FRAME-OPTIONS
属性取值
DENY
:不能被所有网站嵌套或加载;SAMEORIGIN
:只能被同域网站嵌套或加载ALLOW-FROM URL
:可以被指定网站嵌套或加载
安全策略
内容安全策略(CSP)
CSP(Content Security Policy 内容安全策略)
,通过它可以明确的告诉客户端浏览器当前页面的哪些外部资源可以被加载执行,而哪些又是不可以的。
CSP的意义
CSP
防XSS
等攻击的利器。CSP
的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。CSP
大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
CSP的分类
Content-Security-Policy
配置好并启用后,不符合CSP
的外部资源就会被阻止加载Content-Security-Policy-Report-Only
表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri
选项配合使用。
CSP的使用
- 通过
HTTP
头配置Content-Security-Policy
,以下配置说明该页面只允许当前源和https://apis.google.com
这 2 个源的脚本加载和执行:
Content-Security-Policy: script-src 'self' https://apis.google.com
- 通过页面 标签配置
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://apis.google.com">
Sandbox(安全沙箱)
多进程的浏览器架构将主要分为两块:浏览器内核和渲染内核。而安全沙箱能限制了渲染进程对操作系统资源的访问和修改,同时渲染进程内部也没有读写操作系统的能力
,而这些都是在浏览器内核中一一实现了,包括持久存储、网络访问和用户交互等一系列直接与操作系统交互的功能。浏览器内核和渲染内核各自职责分明,当他们需要进行数据传输的时候会通过 IPC
进行
渲染进程的工作是进行 HTML
、CSS
的解析,JavaScript
的执行等,而这部分内容是直接暴露给用户的,所以也是最容易被黑客利用攻击的地方,如果黑客攻击了这里就有可能获取到渲染进程的权限,进而威胁到操作系统。所以需要一道墙用来把不可信任的代码运行在一定的环境中,限制不可信代码访问隔离区之外的资源,而这道墙就是浏览器的安全沙箱。
安全沙箱的存在是为了保护客户端操作系统免受黑客攻击,但是阻止不了 XSS
和 CSRF
。
安全沙箱是利用操作系统提供的安全技术,这样渲染进程在运行中就无法获取或修改操作系统中的数据。安全沙箱最小隔离单位是进程,所以无法保护单进程浏览器。
Iframe
iframe
在给我们的页面带来更多丰富的内容和能力的同时,也带来了不少的安全隐患。因为iframe
中的内容是由第三方来提供的,默认情况下他们不受我们的控制,他们可以在iframe
中运行JavaScirpt
脚本、Flash
插件、弹出对话框等等,这可能会破坏前端用户体验。
如何让自己的网站不被其他网站的iframe引用
js的防御方案
将下面这段代码放到网站页面的</body>
标签前,这样别人在通过iframe
框架引用你的网站网页时,浏览器会自动跳转到你的网站所引用的页面上
<script>
if (self == top) {
var theBody = document.getElementsByTagName('body')[0];
theBody.style.display = "block";
} else {
top.location = self.location;
}
</script>
使用X-Frame-Options
防止网页被iframe
X-FRAME-OPTIONS
是微软提出的一个http
头,专门用来防御利用iframe
嵌套的点击劫持攻击。
DENY // 拒绝任何域加载
SAMEORIGIN // 允许同源域下加载
ALLOW-FROM // 可以定义允许frame加载的页面地址
如何禁止被使用的 iframe
对当前网站某些操作
sandbox
是html5
的新属性,主要是提高iframe
安全系数。iframe
因安全问题而臭名昭著,这主要是因为iframe
常被用于嵌入到第三方中,然后执行某些恶意操作。这个与上面说到的安全沙箱(Sandbox)不同
现在有一场景:我的网站需要 iframe
引用某网站,但是不想被该网站操作DOM
、不想加载某些js
(广告、弹框等)、当前窗口被强行跳转链接等,我们可以设置 sandbox
属性:
allow-same-origin // 允许被视为同源,即可操作父级DOM或cookie等
allow-top-navigation // 允许当前iframe的引用网页通过url跳转链接或加载
allow-forms // 允许表单提交
allow-scripts // 允许执行脚本文件
allow-popups // 允许浏览器打开新窗口进行跳转
"" // 设置为空时上面所有允许全部禁止