携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情
简介
随着互联网的高速发展,信息安全问题已经成为行业最为关注的焦点之一。今天我们就来说说一些常见的攻击方法以及相应的防御措施。希望看完本文大家能对前端安全有更深一层的了解。
SQL 注入
SQL 注入漏洞(SQL Injection)是 Web 开发中最常见的一种安全漏洞。
原理
它主要是利用前端表单提交,比如输入框、富文本框,输入sql语句提交到后端,当后端服务通过提交的字段拼接成sql语句进行数据库查询的时候,恶意sql被执行,从而达到攻击的目的。
可以用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出文件等一系列恶意操作,甚至有可能获取数据库乃至系统用户最高权限。
比如我们的登录,通过接收用户输入的用户名和密码进行sql拼接然后去数据库查询。
# 接收username password
uname = request.POST['username']
passwd = request.POST['password']
# 存在 SQL 注入漏洞的 SQL 查询语句
sql = “SELECT id FROM users WHERE username=’” + uname + “’ AND password=’” + passwd + “’”
# 执行 SQL 语句
database.execute(sql)
我们正常输入肯定没问题,但是如果传入恶意sql呢?
比如当我传入的密码是password' OR 1=1,拼接的sql语句如下
SELECT id FROM users WHERE username='username' AND password='password' OR 1=1'
由于 OR 1=1 语句,无论 username 和 password 是什么,WHERE 分句都将返回 users 表中第一个 id。数据库中第一个用户的 id 通常是数据库管理员。通过这种方式,攻击者不仅绕过了身份认证,而且还获得了管理员权限。因此就达到了攻击的目的。
防御措施
- 转义过滤用户的输入的某些
sql关键字,如select、from、and、or等。 - 使用
ORM框架如MyBatis,减少sql的手动拼接。
XSS 跨站点脚本攻击
XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。
XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
类型
XSS 一般分为存储型、反射型和 DOM 型。
存储型
存储型:顾名思义,黑客将恶意 JavaScript 脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型
反射型:顾名思义,恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后网站又将这部分返回给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中。
反射型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
DOM 型
DOM 型:我们知道,JavaScript 也是可以改变 HTML 的,黑客正是利用这一点来实现插入恶意脚本。
DOM 型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
防御措施
-
尽量少使用
.innerHTML、.outerHTML、document.write(),如果用 Vue/React 技术栈,尽量少使用v-html/dangerouslySetInnerHTML功能。如果一定要使用的话一定要避免在字符串中拼接不可信数据,把用户输入的& < > " ' /等字符转义掉。 -
设置
cookie为HttpOnly。被设置了httpOnly的cookie字段无法通过JS获取,也就降低了XSS攻击时用户凭据隐私泄漏的风险。 -
开启
CSP防护。内容安全策略(CSP)的设计就是为了防御XSS攻击的,通过在HTTP头部或meta中设置Content-Security-Policy,就可以配置该策略。
CSRF 跨站点请求伪造
CSRF 攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
攻击流程
CSRF的攻击流程:
- 受害者登录目标网站A;
- 受害者以某种方式接触到恶意网站B的链接;
- 受害者点击链接访问网站B, 网站B中的js代码执行, 偷偷向目标网站A发送某个请求;
- 由于受害者登录过网站A, 因此请求携带了网站A的相关cookie凭证,最后请求成功执行;
类型
CSRF 攻击类型有GET型、POST型、链接型三种。
GET型
比如在网站中的一个 img 标签里 src 属性构建一个请求,当用户打开这个网站的时候就会自动发起提交。
<img src="http://bank.com/consume?amount=10000&account=randy" />
比如上面的链接是一个扣款链接,当用户在bank.com网站登录后,被不法分子诱导到诈骗网站B,页面加载的时候就会发送请求到bank.com网站,因为用户之前登陆过bank.com所以会直接验证成功。从而达到攻击的目的。
POST型
如果源网站请求是POST类型的话,上面的例子就不适用了,就需要构造表单进行POST请求了。其实原理都是一致的。
比如说构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
<form action="http://bank.com/consume" method=POST>
<input type="hidden" name="account" value="randy" />
<input type="hidden" name="amount" value="10000" />
</form>
<script> document.forms[0].submit(); </script>
这样也能达到攻击的目的。
链接型
第三种是链接类型的 CSRF 攻击,比如说在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
<a href="http://bank.com/consume?amount=10000&account=randy">
点击领取福利</a>
相较于GET型和POST型用户打开页面就中招的情况,这种需要用户再次点击链接才会触发。
CSRF的特点
- 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取登录凭证数据。
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是冒用。
- 跨站请求可以用各种方式:图片URL、超链接、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
防御措施
CSRF 攻击的本质是利用了 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
第一种是同源检测的方法,服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤。当 origin 或者 referer 信息都不存在的时候,直接阻止。这种方式的缺点是有些情况下 referer 可以被伪造。还有就是我们这种方法同时把搜索引擎的链接也给屏蔽了,所以一般网站会允许搜索引擎的页面请求,但是相应的页面请求这种请求方式也可能被攻击者给利用。
第二种方法是使用 CSRF Token 来进行验证,服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证。这种方法解决了使用 cookie 单一验证方式时,可能会被冒用的问题,但是这种方法存在一个缺点就是,我们需要给网站中的所有请求都添加上这个 token,操作比较繁琐。还有一个问题是一般不会只有一台网站服务器,如果我们的请求经过负载平衡转移到了其他的服务器,但是这个服务器的 session 中没有保留这个 token 的话,就没有办法验证了。这种情况我们可以通过改变 token 的构建方式来解决。
第三种方式使用双重 Cookie 验证的办法,服务器在用户访问网站页面时,向请求域名注入一个 Cookie,内容为随机字符串,然后当用户再次向服务器发送请求的时候,从 cookie 中取出这个字符串,添加到 URL 参数中 (http://bank.com?csrfcookie=v8g9e4ksfhw),然后服务器通过对 cookie 中的数据和参数中的数据进行比较,来进行验证。使用这种方式是利用了攻击者只能利用 cookie,但是不能访问获取 cookie 的特点。并且这种方法比 CSRF Token 的方法更加方便,并且不涉及到分布式访问的问题。这种方法的缺点是如果网站存在 XSS 漏洞的,那么这种方式会失效。同时这种方式不能做到子域名的隔离。
第四种方式是在使用 cookie 的时候设置 Samesite 为 Strict,限制 cookie 不能作为被第三方使用,从而可以避免被攻击者利用。Samesite有Strict、Lax、None三种模式。在 Strict 模式下,浏览器完全禁止第三方请求携带 Cookie。比如请求randy.com 网站只能在 randy.com 域名当中请求才能携带Cookie,在其他网站请求都不能。在 Lax 模式,就宽松一点了,但是只能在 get 方法提交表单况或者 a标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能。在 None 模式下,也就是默认模式,请求会自动携带上 Cookie。
DDOS 分布式拒绝服务攻击
DDOS攻击准确来说其实不属于前端范畴,这里我们还是来介绍下,算是前端开发需要了解的一个东西吧。
DDoS(Distributed Denial of Service)即分布式拒绝服务攻击,是目前最为强大、最难以防御的攻击方式之一。要理解DDos,得先从DoS说起。最基本的DoS攻击就是利用合理的客户端请求来占用过多的服务器资源,从而使合法用户无法得到服务器的响应。DDoS攻击手段是在传统的DoS攻击基础之上产生的一类攻击方式,传统的DoS攻击一般是采用一对一的方式,当攻击目标的CPU速度、内存或者网络带宽等各项性能指标不高的情况下,它的效果是明显的。
但随着计算机与网络技术的发展,计算机的处理能力显著增加,内存不断增大,同时也出现了千兆级别的网络,这使得DoS攻击逐渐失去了效果。这时分布式拒绝服务攻击手段(DDoS)便应运而生了。理解了DoS攻击后,DDoS的原理就非常简单了,它指的是攻击者借助公共网络,将数量庞大的计算机设备联合起来作为攻击平台,对一个或多个目标发动攻击,从而达到瘫痪目标主机的目的。通常在攻击开始前,攻击者会提前控制大量的用户计算机,称之为“肉鸡",并通过指令使大量的肉鸡在同一时刻对某个主机进行访问,从而达到瘫痪目标主机的目的。
类型
DDoS攻击种类很多,下面我们简单介绍几种常见的攻击形式。
SYN Flood
我们知道,TCP请求之前会进行三次握手,半连接队列和全连接队列就是在握手期间产生的。
半连接队列
当客户端发送 SYN 到服务端,服务端收到以后回复 ACK 和 SYN,状态由 LISTEN 变为 SYN_RCVD,此时这个连接就被推入了 SYN 队列,也就是半连接队列。
全连接队列
当客户端返回 ACK, 服务端接收后,三次握手完成。这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列(Accept Queue)。
SYN Flood 攻击原理
SYN Flood正是利用了TCP协议三次握手的过程来达成攻击的目的。攻击者伪造大量的IP地址给服务器发送SYN报文,但是由于伪造的IP地址几乎不可能存在,也就不可能从客户端得到任何回应,服务端将维护一 个非常大的半连接等待列表,并且不断对这个列表中的IP 地址进行遍历和重试,占用了大量的系统资源。更为严重的是,由于服务器资源有限,大量的恶意客户端信息占满了服务器的等待队列,导致服务器不再接收新的SYN请求,正常用户无法完成三次握手与服务器进行通信,这便是SYN Flood攻击。
CC攻击
CC ( Challenge Collapsar)攻击属于DDos的一 种,是基于应用层 HTTP 协议发起的 DDos攻击,也被称为HTTP Flood.
CC攻击的原理是这样的,攻击者通过控制的大量 “肉鸡”或者利用从互联网上搜寻的大量知名的HTTP代理,模拟正常用户给网站发起请求直到该网站拒绝服务为止。大部分网站会通过CDN以及分布式缓存来加快服务端响应,提升网站的吞吐量,而这些精心构造的HTTP请求往往有意避开这些缓存,需要进行多次DB查询操作或者一次请求返回大量的数据,加速系统资源消耗,从而拖垮后端的业务处理系统,甚至连相关存储与日志收集系统也无法幸免。
cc攻击发起容易,防范困难,影响却广泛,是近年来 DDos攻击的主流方式。
cc攻击并不需要攻击者控制大量的 “肉鸡”,取而代之的是互联网上十分容易找到的各种 HTTP 代理,“肉鸡” 由于流量异常,容易被管理人员发现,攻击持续时间难以延续,而使用 HTTP 代理则使攻击者能够发起持续高强度的攻击。攻击在应用层发起,往往又与网站的业务紧密相连,使得防守一方很难在不影响业务的情况下对攻击请求进行过滤,大量的误杀将影响到正常访问的用户,间接地达成攻击者的目的。
防御措施
如何应对 SYN Flood 攻击?
- 增加 SYN 连接,也就是增加半连接队列的容量。
- 减少 SYN + ACK 重试次数,避免大量的超时重发。
- 利用 SYN Cookie 技术,在服务端接收到 SYN 后不立即分配连接资源,而是根据这个 SYN 计算出一个 Cookie,连同第二次握手回复给客户端,在客户端回复 ACK 的时候带上这个 Cookie 值,服务端验证 Cookie 合法之后才分配连接资源。
如何应对cc攻击?
- 限制单 IP 请求频率。
- 网络架构上做好优化,采用负载均衡分流。
- 增加 WAF(Web Application Firewall)设备,WAF 的中文名称叫做 Web 应用防火墙。Web 应用防火墙是通过执行一系列针对 HTTP / HTTPS 的安全策略来专门为 Web 应用提供保护的一款产品。
- 使用 CDN / 云清洗,在攻击发生时,进行云清洗。通常云清洗厂商策略有以下几步:预先设置好网站的 CNAME,将域名指向云清洗厂商的 DNS 服务器;在一般情况下,云清洗厂商的 DNS 仍将穿透 CDN 的回源的请求指向源站,在检测到攻击发生时,域名指向自己的清洗集群,然后再将清洗后的流量回源。
- 或者买各大云服务厂商的服务,比如阿里云的
SCDN。
扩展
CSP
CSP 就是白名单,作用是可以禁止加载外域的代码,禁止外域的提交,只允许本域下的请求表单提交之类的。简单说就是告诉浏览器哪些资源可以加载执行,让那些真的插入进入的恶意代码也不会被执行;或者允许向哪些第三方站点提交数据,因为攻击者窃取信息的根本还是向外域提交。
开启 CSP 的方式有两种
设置 html meta
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" />
设置 http response header
# 所加载的内容必须来自同源站点
Content-Security-Policy: default-src "self"
# 添加可信域名
Content-Security-Policy: *.baidu.com
如果将CSP设置成以下模式:
Content-Security-Policy: script-src 'self'
那么该网站将:
- 不允许内联脚本执行;
- 禁止加载外域js代码;
- 禁止外域提交;
这将有效地防范XSS的攻击,当然他也非常严格,可能会对自身的业务开发也造成一定限制,更多关于CSP的内容可以查看CSP MDN。
HSTS
HSTS 是 HTTP 严格传输安全协议,作用是强制客户端使用 https 与服务器建立连接,以避免因为 http 而被中间人攻击。
HSTS 设置须添加在请求头中,如下
Strict-Transport-Security: max-age=12345;includeSubDomains;
参数:
- max-age: 指定该设置过期的时间,单位毫秒
- includeSubDomains: 可选参数,表示所有子域名也必须通过
https访问 - preload: 可选参数,可以设置一个使用
https的域名列表
设置 HSTS 后,再使用 http 访问时,只要 max-age 没有过期,客户端内部会进行跳转,会出现 307 Redirect Internel 状态码,变成 https 后,再访问请求的资源服务器
X-Frame-Options
X-Frame-Options 是用于控制当前页面是否可以被嵌入到 iframe 中,以防盗链及点击劫持攻击
X-Frame-Options 需要配置在响应头中
X-Frame-Options: DENY
参数:
- DENY: 不允许,同域名嵌套也不行
- SAMEORIGIN: 允许同域名嵌套
- ALLOW_FORM url: 可以指定允许嵌套访问的来源
设置 X-Frame-Options 后,可以确保我们的网站没有被嵌入到别人的站点里去(以防内容被恶意嵌套,并且在表面加一个透明层,诱导用户点击),从而避免点击劫持攻击及恶意盗链。
SRI
SRI 指子资源完整性,该方案的作用是确保我们站点的资源文件永远不会被改变,如果被改变,浏览器会拒绝执行
比如我们开源了一个 xxx.js 文件,并上传到 CDN。假如A系统使用了我们的开源 xxx.js 文件,用户在访问A系统的时候,会去请求 xxx.js,而这个文件可能被劫持篡改,或者由于网络等原因,接收到的文件不完整,怎么办?
而设置了 SRI 就可以确保当请求的文件被篡改或不完整的时候就拒绝执行该文件,设置如下:
<link href="https://xxx/x.css" intergrity="sha1/asdhfkjasdf">
<script src="https://xxx/x.js" intergrity="sha1/asdhfkjasdf"></script>
标签上的 intergrity 属性值的格式是:哈希算法/base64后的哈希值
原理:
打包的时候会根据文件内容生成 hash,并且把 hash 作为 intergrity 属性注入到标签上,客户端接收到文件后,根据文件内容生成 hash 与 intergrity 上的进行对比是否一致,如果不一致就会认为是不安全的,拒绝执行
Referrer-Policy
Referrer-Policy 是一种 HTTP 安全方案,用于控制 referer 携带策略,用于监管哪些访问来源信息会在 referer 中发送。
Referer 是一个请求头,内容为当前发送请求的域名及端口号及参数,请求资源的完整URI(客户端)。比如https:/localhost:8081/link?query=xxxxx
注意 referer 实际上是单词 referrer 的错误拼写。而 Referrer-Policy 这个首部并没有延续这个错误拼写
设置 Referrer-Policy 的方式有如下方式
设置 html 元素的referer策略
你也可以在 HTML 内设置 referrer 策略。例如,你可以用一个 name 为 referrer 的 <meta> 元素为整个文档设置 referrer 策略。
<meta name="referrer" content="origin">
或者用 <a>、<area>、<img>、<iframe>、<script> 或者 <link> 元素上的 referrerpolicy 属性为其设置独立的请求策略。
<a href="http://example.com" referrerpolicy="origin">
另外也可以在 <a>、<area> 或者 <link>元素上将 rel 属性设置为 noreferrer。
<a href="http://example.com" rel="noreferrer">
设置 http 请求头 Referrer-Policy
Referrer-Policy: same-origin
参数:
- no-referrer:不发送
referer - no-referrer-when-downgrade:默认值,同安全级别发送
referrer并且是完整url,不同安全级别则不发送 - origin:只发送协议、域名、端口,不发送完整
url,比如https://juejin.cn/index.html,会变成https//juejin.cn/ - origin-when-cross-origin:同源才发送完整
url,不同源只发送协议、域名、端口 - same-origin:同源才发送
referer,并且是完整的url - strict-origin:同安全级别和
origin是一样的,比如https请求https,如https请求http是不同安全级别了,就不发送 - strict-origin-when-cross-origin:同源发送完整
url,同安全级别才发送协议、域名、端口,不同源或不同安全级别不发送referer - unsafe-url:这是最不安全的策略,不管同不同源都发送完整
url
如果你要为那些策略未获广泛的浏览器支持的情况指定一种后备策略,使用逗号分隔的列表,并将希望使用的策略放在最后:
Referrer-Policy: no-referrer, strict-origin-when-cross-origin
在上面的场景中,no-referrer 仅在 strict-origin-when-cross-origin 不被浏览器支持的情况下被使用。
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!