前端安全

常见的前端安全问题

  • iframe
  • opener
  • csrf跨站请求伪造
  • xss跨站脚本攻击
  • 网络劫持(DNS,Http)

关于iframe安全问题

  • 场景一,我们引入或被迫引入(网络劫持)别人的iframe

开发中,我们需要第三方组件,通过iframe引入,由于第三方不可控,带来的问题也会很多,比如第三方域名因为过期而被恶意攻击者抢注,或者第三方被黑客攻破,iframe中的内容别替换掉了,从而利用用户浏览器中的安全漏洞下载安装木马、恶意勒索软件等等.
这种时候,我们就要对第三方有足够的限制权,才能保障自己网站的安全.

防御策略

html5的新属性sandbox,主要是提高iframe安全系数。

1
2
3
4
5
6
7
8
<iframe sandbox src="..."> ... </iframe>
/*
如果你只是添加上这个属性而保持属性值为空,那么浏览器将会对iframe实施史上最严厉的调控限制
allow-forms: 允许iframe中提交form表单
allow-popups: 允许iframe中弹出新的窗口或者标签页(例如,window.open(),showModalDialog(),target=”_blank”等等)
allow-scripts: 允许iframe中执行JavaScript
allow-same-origin: 允许iframe中的网页开启同源策略
*/
  • 场景二,我们的页面被别人以iframe形式引用

当我们开发出精美的展示类页面,有些人会盗取我们的劳动成功,将我们精美的展示内容通过iframe引入到他们的网站.当然,只是展示的话问题不大,但如果我们的一些涉及安全隐私的页面被引入,诱导用户操作,获取用户信息就不太好了.
这时我们需要限制自己页面被引用

防御策略
  • 页面操作

在自己页面加一下代码,禁止自己页面被iframe引入

1
2
3
if (self!=top){
window.top.location.replace(self.location); //打开自己网站的页面
}

如果我们要引入自己的iframe组件,可以加层判断,不同域时才禁止iframe

1
2
3
4
5
6
7
8
9
10
try{
  top.location.hostname; //检测是否出错
  //如果没有出错,则降级处理
  if (top.location.hostname != window.location.hostname) {
    top.location.href =window.location.href;
  }
}
catch(e){
  top.location.href = window.location.href;
}
  • 服务器设置

使用X-Frame-Options: DENY这个HTTP Header相应头来明确的告知浏览器,不要把当前HTTP响应中的内容在HTML Frame中显示出来。

参数:

1
2
3
DENY:当前页面不能被嵌套iframe里,即便是在相同域名的页面中嵌套也不允许,也不允许网页中有嵌套iframe
SAMEORIGIN:iframe页面的地址只能为同源域名下的页面
ALLOW-FROM:可以在指定的origin url的iframe中加载

opener

如果在项目中需要 打开新标签 进行跳转一般会有两种方式

1
window.open('http://www.xxx.com')
1
<a target='_blank' href='http://www.xxx.com'>

通过这两种方式打开的页面可以使用 window.opener 来访问源页面的 window 对象。
场景:A 页面通过 <a> 或 window.open 方式,打开 B 页面。但是 B 页面存在恶意代码如下
window.opener.location.replace(‘https://www.bbb.com')
此时,用户正在浏览新标签页,但是原来网站的标签页已经被导航到了其他页面。
即使在跨域状态下 opener 仍可以调用 location.replace 方法。

防御策略
1
2
3
4
5
6
<a target="_blank" href="" rel="noopener noreferrer nofollow">a标签跳转url</a>
<!--
noopener:会将 window.opener 置空
norefferrer: 兼容火狐
nofollow:SEO权重优化(意思是告诉搜索引擎不要此链接或不要追踪此特定链接。)
-->
1
2
3
4
5
function openurl(url) {
var newTab = window.open();
newTab.opener = null;
newTab.location = url;
}

CSRF / XSRF跨站请求伪造

CSRF的重点是伪造,并不会直接对网站或用户进行攻击,一旦得逞,危害甚广.
大致场景:

  1. 我们的用户登录我们的网站并保留了登录凭证
  2. 攻击者诱导用户访问恶意网站
  3. 恶意网站向我们的网站发送请求,其中默认携带了事先保存的登录凭证,绕过了我们的验证
  4. 我们响应了恶意网站伪造的请求,攻击者得逞

几种常见的CSRF攻击类型
  • GET类型

利用img,javascript等能跨域标签,在恶意网站发送请求到银行,其中携带登录信息.

1
<img src="http://bank.example/withdraw?amount=10000&for=hacker" > 
  • POST类型

通常使用的是一个自动提交的表单,相当于模拟用户完成了一次POST操作

1
2
3
4
5
6
 <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>
  • 链接类型的CSRF

诱导用户点击到恶意网站

1
2
3
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
重磅消息!!
<a/>

CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。

防护策略

根据CSRF通常来自外域和冒充用户登录凭证(cookie)的特点,可以从两个方面出发:

  • 阻止不明外域访问
    • Origin Header: 请求的Header中会携带Origin字段。字段内包含请求的域名,使用Origin中的字段确认来源域名
    • Referer Header: 在 HTTP 头中有一个字段叫Referer,它记录了该 HTTP 请求的来源地址。通过验证Referer,可以检查请求是否来自合法的”源”。
  • 加强验证策略
    • 验证码
    • token验证: 服务端随机生成token,保存在服务端session中,同时保存到客户端中,客户端发送请求时,把token带到HTTP请求头或参数中,服务端接收到请求,验证请求中的token与session中的是否一致。攻击者伪造请求不带token或错误的token,则认为可能是 CSRF 攻击而拒绝该请求。

XSS/CSS(跨站脚本攻击)

XSS是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的客户端上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。XSS在前后端不分离的开发模式中更易发生.因为由模板渲染,渲染在服务端,恶意代码被植入,到客户端解析被执行.

XSS分类
  • 储存型
    例: 评论功能,攻击者评论内容为恶意代码,评论成功后保存在数据库,用户客户端浏览到该评论页,恶意代码被加载执行
  • 反射型
    例: 我们的页面采用模板渲染,代码:
    1
    <a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳转...</a>
    攻击者将我们页面链接参数添加恶意代码http://xxx/?redirect_to=javascript:alert('XSS')
    客户端渲染后,点击跳转便XSS攻击成功
  • DOM型
    例: 同反射型,只不过前端渲染,恶意构造链接是http://xxx/?redirect_to="><script>alert(1)</script>“

DOM 中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 内联事件监听器中包含恶意代码 -->
<img onclick="UNTRUSTED" onerror="UNTRUSTED" src="data:image/png,">

<!-- 链接内包含恶意代码 -->
<a href="UNTRUSTED">1</a>

<script>
// setTimeout()/setInterval() 中调用恶意代码
setTimeout("UNTRUSTED")
setInterval("UNTRUSTED")

// location 调用恶意代码
location.href = 'UNTRUSTED'

// eval() 中调用恶意代码
eval("UNTRUSTED")
</script>
防御策略

根据XSS提交恶意代码和浏览器执行恶意代码的特点

  • 输入,输出检测,充分过滤和转译
  • 前后端分离的形式一定程度能降低防御成本
  • 前端用innerText,避免innerHtml
  • 避免内联形式的代码
  • 限制输入长度通常能提升XSS成本,使其不易成功
  • 使用HttpOnly,保障服务端cookie前端不能访问,避免XSS攻击获取

现代大部分浏览器都自带 XSS 筛选器,vue / react 等成熟框架也对 XSS 进行一些防护


网络劫持

通常都是网络运营商的行为.

  • DNS劫持(访问a链接,却跳转到b页面)
    • DNS强制解析: 通过修改运营商的本地DNS记录,来引导用户流量到缓存服务器
    • 302跳转的方式: 通过监控网络出口的流量,分析判断哪些内容是可以进行劫持处理的,再对劫持的内存发起302跳转的回复,引导用户获取内容
  • Http劫持(访问我们的页面但是一直有贪玩蓝月的广告)
    • 由于http明文传输,运营商会修改你的http响应内容(即加广告)
预防策略

DNS劫持由于涉嫌违法,已经被监管起来,现在很少会有DNS劫持,而http劫持依然非常盛行.
最有效的办法就是全站HTTPS,将HTTP加密,这使得运营商无法获取明文,就无法劫持你的响应内容.
需要注意的是非全站HTTPS并不安全,比如首页http其他页是https,依然能从http页面截取信息


参考:

浅谈前端安全
前端安全系列(一):如何防止XSS攻击?
前端安全系列之二:如何防止CSRF攻击?
网络劫持和HTTPS的安全