前端安全问题总结(一)

很多时候我们在做开发的时候并不会主要考虑安全问题,但是现在从事的行业不同,前端安全也会相应的受到更多的重视,下面通过几个个方面具体了解和分析一下前端的安全相关问题:

  1. 熟悉的CSRF(跨站请求伪造)
  2. 老生常谈的XSS(跨站脚本攻击)
  3. iframe带来的风险
  4. 打开新页面也可能有风险

1、CSRF(跨站请求伪造)
攻击者盗用了用户身份,拿到了登录信息,以用户的名义进行恶意请求。危险is watching you:以用户的名义发送邮件、发信息、盗取账号、购买商品、虚拟货币转账等。总结起来就是:个人隐私暴露及财产安全问题。
CSRF 攻击思想:

  1. 浏览并登录信任网站(举例:淘宝)
  2. 登录成功后在浏览器产生信息存储(举例:cookie)
  3. 用户在没有登出淘宝的情况下,访问危险网站
  4. 危险网站中存在恶意代码,代码为发送一个恶意请求(举例:购买商品/余额转账)
  5. 携带刚刚在浏览器产生的信息进行恶意请求
  6. 淘宝验证请求为合法请求(区分不出是否是该用户发送)
  7. 达到了恶意目标

防御措施(推荐添加token / HTTP头自定义属性)

  • 涉及到数据修改操作严格使用 post 请求而不是 get 请求
  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。
  • 前端安全问题总结(一)
  • HTTP 协议中使用 Referer 属性来确定请求来源进行过滤(禁止外域)

   前端安全问题总结(一)

   前端安全问题总结(一)

  • 请求地址添加 token ,使黑客无法伪造用户请求
  • HTTP 头自定义属性验证(类似上一条)
  • 显示验证方式:添加验证码等

2、XSS(跨站脚本攻击)
XSS是跨站脚本攻击(Cross-Site Scripting)的简称,XSS这类安全问题发生的本质原因在于,浏览器错误的将攻击者提供的用户输入数据当做JavaScript脚本给执行了。

XSS分为:存储型 、反射型 、DOM型XSS

存储型XSS:存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie

前端安全问题总结(一)

 

反射型XSS:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。反射型XSS大多数是用来盗取用户的Cookie信息。

前端安全问题总结(一)


DOM型XSS:不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。

前端安全问题总结(一)

 

例子1:
公司需要一个搜索页面,根据 URL 参数决定关键词的内容。
代码如下:

<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
  您搜索的关键词是:<%= getParameter("keyword") %>
</div>

上线后不久,收到安全组发来的一个神秘链接:

http://xxx/search?keyword="><script>alert('XSS');</script>


当浏览器请求 http://xxx/search?keyword="><script>alert('XSS');</script> 时,服务端会解析出请求参数 keyword,得到 "><script>alert('XSS');</script>,拼接到 HTML 中返回给浏览器。形成了如下的 HTML:

<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
    您搜索的关键词是:"><script>alert('XSS');</script>
</div>
// 卒~~


例子2:
公司需要一个根据 URL 参数决定决定跳转地址。代码如下:

<a :href="getParameter("redirect_to")">跳转...</a>


继续收到安全组神秘链接: 

http://xxx/?redirect_to=javascript:alert('XSS')


解析后:

<a href="javascript:alert('XSS';)">跳转...</a> // 卒~~

可能会被利用的地方

HTML 中内嵌的文本中:<div>{'</div><script>alert('XSS');</script><div>'}</div>
元素的属性(href、src 等):<a href={javascript:alert('XSS')}></a>
元素事件(onload、onerror、onclick 等):<img onl oad={javascript:alert('XSS')}></img>
URL的query:http://abc.com/xxx?search=<script>alert('XSS');</script>
style属性值:background-image:url("javascript:...");

如何防御:

编码:

把可运行的恶意代码编码成不可运行的单纯的数据

前端安全问题总结(一)

 

注:由于用户提交的数据可能会被使用到不同的场景,不同的场景不同的处理方式是不一样;甚至,同一个数据,可能会在不同的场景下使用。所以,在拿到数据之后往往不会立刻做处理。

相反的,在把数据render到HTML的时候,对数据进行处理,是我们较好处理时机。
根据显示场景的不同可以有不同的编码方式例如:

在HTML中显示: Node.textContent = userInput
在URL中使用:Window.encodeURIComponent(userInput)
作为属性使用:Element.style.XXX = userInput/Element.setAttribute(attribute, userInput)
JSON编码等


HTML编码及转换方法:

 

 1 var HtmlUtil = {
 2         /*1.用浏览器内部转换器实现html编码(转义)*/
 3         htmlEncode:function (html){
 4             //1.首先动态创建一个容器标签元素,如DIV
 5             var temp = document.createElement ("div");
 6             //2.然后将要转换的字符串设置为这个元素的innerText或者textContent
 7             (temp.textContent != undefined ) ? (temp.textContent = html) : (temp.innerText = html);
 8             //3.最后返回这个元素的innerHTML,即得到经过HTML编码转换的字符串了
 9             var output = temp.innerHTML;
10             temp = null;
11             return output;
12         },
13         /*2.用浏览器内部转换器实现html解码(反转义)*/
14         htmlDecode:function (text){
15             //1.首先动态创建一个容器标签元素,如DIV
16             var temp = document.createElement("div");
17             //2.然后将要转换的字符串设置为这个元素的innerHTML(ie,火狐,google都支持)
18             temp.innerHTML = text;
19             //3.最后返回这个元素的innerText或者textContent,即得到经过HTML解码的字符串了。
20             var output = temp.innerText || temp.textContent;
21             temp = null;
22             return output;
23         },
24         /*3.用正则表达式实现html编码(转义)*/
25         htmlEncodeByRegExp:function (str){  
26              var temp = "";
27              if(str.length == 0) return "";
28              temp = str.replace(/&/g,"&amp;");
29              temp = temp.replace(/</g,"&lt;");
30              temp = temp.replace(/>/g,"&gt;");
31              temp = temp.replace(/\s/g,"&nbsp;");
32              temp = temp.replace(/\'/g,"&#39;");
33              temp = temp.replace(/\"/g,"&quot;");
34              return temp;
35         },
36         /*4.用正则表达式实现html解码(反转义)*/
37         htmlDecodeByRegExp:function (str){  
38              var temp = "";
39              if(str.length == 0) return "";
40              temp = str.replace(/&amp;/g,"&");
41              temp = temp.replace(/&lt;/g,"<");
42              temp = temp.replace(/&gt;/g,">");
43              temp = temp.replace(/&nbsp;/g," ");
44              temp = temp.replace(/&#39;/g,"\'");
45              temp = temp.replace(/&quot;/g,"\"");
46              return temp;  
47         },
48         /*5.用正则表达式实现html编码(转义)(另一种写法)*/
49         html2Escape:function(sHtml) {
50              return sHtml.replace(/[<>&"]/g,function(c){return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c];});
51         },
52         /*6.用正则表达式实现html解码(反转义)(另一种写法)*/
53         escape2Html:function (str) {
54              var arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"'};
55              return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){return arrEntities[t];});
56         }
57     };

 

验证:
对我们接收到的数据进行过滤,过滤出恶意代码,得到合法代码。

前端安全问题总结(一)


- 黑名单

黑名单,即列举了恶意代码类型的名单。如果数据在黑名单里面,则被判定为非法代码;否则为合法代码。

缺陷:因为现实是复杂的,攻击者的手法是千奇百怪的,要创建一个包含所有可能的恶意代码的黑名单,往往是困难,甚至是不能的,很难做到全面覆盖,而且时效性短。

- 白名单

白名单是包含了合法代码的名单。通常来说,合法的情况是可以穷尽的,比如一个输入用户名的input,我就只接受字母,数字,下划线这三种类型的字符。

优势:可覆盖所有的合法场景,时效性长


验证结果处理 - 舍弃

当我们检测出用户的输入里面包含恶意代码的时候,直接舍弃掉这一条数据,这条数据不会出现在网站的任何地方,即叫做舍弃。

例如,有一个需要用户输入信用卡号码的input,用户不仅输入了数字,还输入了连接线(用户出于习惯)。我们直接舍弃这条数据,把这条数据判定为不合法。当然,相应地,你会给用户返回一个错误提醒。

验证结果处理 - 消毒

当我们检测出用户的输入里面包含恶意代码的时候,经过一定的处理方式,去掉恶意的部分,留下合法的代码,即叫做消毒。

还是用户输入信用卡号码的例子。对比舍弃的做法,消毒的做法是允许用户输入连接线,只是我们拿到数据之后,把连接线给去掉,最后得到只包含数字的信用卡号码

xss小游戏:

https://alf.nu/alert1 

 

3、iframe使用
a. 如何让自己的网站不被其他网站的 iframe 引用?(点击劫持--clickjacking)
利用透明 iframe 覆盖原网页诱导用户进行某些操作达成目的,进行点击劫持攻击。通常的攻击步骤是这样的:

  1. 攻击者精心构造一个诱导用户点击的内容,比如Web页面小游戏
  2. 将我们的页面放入到iframe当中
  3. 利用z-index等CSS样式将这个iframe叠加到小游戏的垂直方向的正上方
  4. 把iframe设置为100%透明度
  5. 受害者访问到这个页面后,肉眼看到的是一个小游戏,如果受到诱导进行了点击的话,实际上点击到的却是iframe中的我们的页面
  6. 点击劫持的危害在于,攻击利用了受害者的用户身份,在其不知情的情况下进行一些操作。如果只是迫使用户关注某个微博账号的话,看上去仿佛还可以承受,但是如果是删除某个重要文件记录,或者窃取敏感信息,那么造成的危害可就难以承受了。

如何防御

  • 在HTTP header中加入 X-FRAME-OPTIONS 属性:该响应头属性是用来给浏览器指示允许一个页面可否在<frame>,<iframe>,<embed>或者<object>中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面。

          DENY:不能被所有网站嵌套或加载;
          SAMEORIGIN:只能被同域网站嵌套或加载;
          ALLOW-FROM URL:可以被指定网站嵌套或加载。

  • 判断当前网页是否被 iframe 嵌套
// 检测当前网站是否被第三方iframe引用
// 若相等证明没有被第三方引用,若不等证明被第三方引用。当发现被引用时强制跳转百度。
if(top.location != self.location){
    top.location.href = self.location
}

 

b. 如何防御被使用的 iframe 对当前网站某些操作
有些时候我们的前端页面需要用到第三方提供的页面组件,通常会以iframe的方式引入。例如使用iframe在页面上添加第三方提供的广告、天气预报、社交分享插件等等。
iframe在给我们的页面带来更多丰富的内容和能力的同时,也带来了不少的安全隐患。因为iframe中的内容是由第三方来提供的,默认情况下他们不受我们的控制,他们可以在iframe中运行JavaScirpt脚本、Flash插件、弹出对话框等等,这可能会破坏前端用户体验。
如何防御
在HTML5中,iframe有了一个叫做sandbox的安全属性,通过它可以对iframe的行为进行各种限制,充分实现“最小权限“原则。使用sandbox的最简单的方式就是只在iframe元素中添加上这个关键词就好,就像下面这样:

<iframe sandbox src="..."> ... </iframe>

当sandbox保持属性值为空,那么浏览器将会对iframe实施史上最严厉的调控限制,基本上来讲就是除了允许显示静态资源以外,其他什么都做不了。比如不准提交表单、不准弹窗、不准执行脚本等等,连Origin都会被强制重新分配一个唯一的值,换句话讲就是iframe中的页面访问它自己的服务器都会被算作跨域请求。
另外,sandbox也提供了丰富的配置参数,我们可以进行较为细粒度的控制。一些典型的参数如下:
allow-forms:允许iframe中提交form表单
allow-popups:允许iframe中弹出新的窗口或者标签页(例如,window.open(),showModalDialog(),target=”_blank”等等)
allow-scripts:允许iframe中执行JavaScript
allow-same-origin:允许iframe中的网页开启同源策略

 

4、打开新页面也可能有风险--opener
在项目中需要 打开新标签 进行跳转一般会有两种方式

// 1) HTML -> <a target='_blank' href='http://www.baidu.com'>
// 2) JS -> window.open('http://www.baidu.com')
/*
* 这两种方式看起来没有问题,但是存在漏洞。
* 通过这两种方式打开的页面可以使用 window.opener 来访问源页面的 window 对象。
* 场景:
* A 页面通过 <a> 或 window.open 方式,打开 B 页面。但是 B 页面存在恶意代码如下:
* window.opener.location.replace('https://www.baidu.com') 【此代码仅针对打开新标签有效】
* 此时,用户正在浏览新标签页,但是原来网站的标签页已经被导航到了百度页面。
* 恶意网站可以伪造一个足以欺骗用户的页面,使得进行恶意破坏。
* 即使在跨域状态下 opener 仍可以调用 location.replace 方法。
*/

 

如何防御
a、<a target="_blank" href="">

<a target="_blank" href="" rel="noopener noreferrer nofollow">a标签跳转url</a>
<!-- 
  通过 rel 属性进行控制:
  noopener:会将 window.opener 置空,从而源标签页不会进行跳转(存在浏览器兼容问题)
  noreferrer:兼容老浏览器/火狐。禁用HTTP头部Referer属性(后端方式)。
  nofollow:SEO权重优化,详情见 https://blog.csdn.net/qq_33981438/article/details/80909881
 -->


b、window.open()

<button onclick='openurl("http://www.baidu.com")'>click跳转</button>

function openurl(url) {
    var newTab = window.open();
    newTab.opener = null;
    newTab.location = url;
}

 

 

 

 

 


引用:
https://zhuanlan.zhihu.com/p/83865185
https://www.jianshu.com/p/734bcceb4f36
https://segmentfault.com/a/1190000016551188
https://insights.thoughtworks.cn/eight-security-problems-in-front-end/

上一篇:JavaScript正则表达式replace的一个坑


下一篇:vue自定义指令防抖和节流