前言
这周末应该有很多人被叫去加班改bug了吧,因为出现了一个log4j的bug,可以让黑客进行攻击,所以都在紧急修复这个问题。
现在互联网的开发者最关心的问题是是什么?肯定是安全。不止是开发者会关心安全问题,就是普通用户也会关心这个问题,如果一个网站或者一个APP不安全,用户是不会选择使用的,谁愿意让自己的信息公布于众,而随着技术的发展各种各样的攻击手段层出不穷。这里有一个网站,隔一段时间就会统计各种web攻击的排名前十:owasp,感兴趣的可以点进去看看,下面我先列出2017年和2021年攻击手段排名top10:
从图上可以看到作为一名前端开发者经常听到的一些攻击:XSS、Injection等等,为啥这里面没有CSRF呢,目前为止我们对CSRF的防范已经很成熟了,现在使用的token就是为了防止CSRF的攻击而产生,而且它的攻击手段简单,不像XSS难以防范,所以已经跌出前十了,在前几年还能排进前十。
XSS
下面就开始讲我们本篇文章的重点了,那就是XSS(跨站脚本攻击)。先来看一下XSS的定义:XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
我们想个问题:如果你能够在别人的网站里植入JS,你会干嘛?
如果我们可以在一个网站里植入自己的js代码,我们就拥有了无限的可能,比如:无限弹窗、篡改网站页面、获取用户信息等等。甚至你可以在网站里植入广告,以此来获取大量利润,这都是完全可以实现的。所以XSS的危害是巨大的,作为一个前端开发人员不得不小心。
XSS分类
根据攻击的来源可以分为三种类型:存储型、反射型、DOM型三种。
存储型XSS
存储型 XSS 的攻击步骤:
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
- 这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型XSS
反射型 XSS 的攻击步骤:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。
DOM型XSS
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
攻击举例
随便在一个html文件里写一个input和button,假设现在是一个文章的评论区,那么我们在这个input框里输入的文字就是我们要评论的内容,点击按钮发表评论,发表的内容会在下面展示出来,我们先测试一下:
很完美,可以打印出来了,那么普通文本可以展示出来,换成标签呢,我们在试试:
可以看到我们输入s标签就是加横线的,下面打印出来的和我们输入的不一样,这里我是使用的innerHTML展示的输入框内容。如果我们在一个网站里输入东西之后展示的是这样的结果,那么我们就可以做很多事情了,因为这代表着这个网站没有对XSS进行防御,我们可以为所欲为,下面就进行一个弹窗的测试,更直观的展示使用document.write()来实现,innerHTML需要一些特殊操作:
如上图所示,在输入框中输入一行js代码,点击打印文字就会执行。不要仅仅以为只能执行这几行js代码感觉做不了什么事情,script标签的src属性可以链接外部文件。加入一个黑客写了一堆代码,然后使用src加载这个文件并执行,这是一件非常可怕的事情。上面的演示只是本地模拟,真正的场景是会把评论内容存进数据库,这样会造成每个看到这个评论的用户都会执行这句恶意注入的代码,这就会造成很严重的影响,下面我们再来看一个改变DOM结构的XSS攻击:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="inp">
<button id="btn">打印文字</button>
<div id="box"></div>
<script>
let inp = document.getElementById('inp');
let btn = document.getElementById('btn');
let box = document.getElementById('box');
btn.onclick = function() {
let value = inp.value;
box.innerHTML = "<img src='"+value+"'></img>";
}
</script>
</body>
</html>
这是我们要测试的代码,可以看到这行代码里引入了一个img标签,输入的内容会作为这个img的src,这个场景也很常见,比如头像上传,图片上传等等。
我们在input框中输入1'onerror='alert("xss")
也会执行这些代码,1’是为了闭合img标签的src,否则后面的js代码无法生效。我们甚至可以在输入框中执行一个函数添加一个诱导链接,然后获取用户信息。
XSS防御
通过上面的举例和分类可以看出xss攻击有两大要素:
- 攻击者提交恶意代码
- 浏览器执行恶意代码
解决方法:从输入到输出都要进行过滤、转义。永远不要相信用户的输入!!!
输入
- 用户输入
- url参数
- post参数
我们需要对一些特殊的字符、标签进行编码展示:
输出
永远不要相信innerHTML、document.write()、eval这三兄弟,只要网站中使用了这三种输出方式,那么恭喜你,喜提XSS攻击,这三种输出方式可以做太多的事情。
CSP(Content Security Policy)
严格的 CSP 在 XSS 的防范中可以起到以下的作用:
- 禁止加载外域代码,防止复杂的攻击逻辑。
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
- 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
- 合理使用上报可以及时发现 XSS,利于尽快修复问题。
后记
大家在进行网站开发时一定不要盲目自信,尽量使用完善的防御工具或者防御库,在开发完成后请测试人员或者安全人员进行测试,确保没有问题后在进行上线,否则可能会带来一系列的灾难。
最后给大家提供一个XSS小游戏,一共有6关,都是实现XSS攻击的,每完成一个攻击就会进入下一关,要是通关了,我想你开发的网站在XSS防御方面应该是比较可靠的:xss小游戏