XSS(跨站脚本攻击)
XSS,即 Cross Site Script,中译是跨站脚本攻击;其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS。

XSS 攻击是指攻击者在网站上注入恶意的客户端代码,诱导用户进行对应操作后,可以JS调用cookie等用户信息

有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将用户重定向到一个由攻击者控制的网站,在用户的客户端上进行一些恶意操作

XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM。

反射型

反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接(比如 email, 网站的私信、评论等地方引导用户点一个链接),或者提交一个表单。

最简单的示例是访问一个链接,服务端返回一个可执行脚本:

  1. const http = require('http');
  2. function handleReequest(req, res) {
  3. res.setHeader('Access-Control-Allow-Origin', '*');
  4. res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
  5. # 点击这个链接,链接返回的信息,是执行jsjs读取cookie
  6. res.write('<script>alert("反射型 XSS 攻击", document.cookie)</script>');
  7. res.end();
  8. }
  9. const server = new http.Server();
  10. server.listen(8001, '127.0.0.1');
  11. server.on('request', handleReequest);

存储型

存储型 XSS 会把用户输入的数据 “存储” 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行

这种 XSS 攻击具有很强的稳定性。
比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,
文章或评论发表后,所有访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码:

  1. // 例如在评论中输入以下留言
  2. // 如果请求这段留言的时候服务端不做转义处理,请求之后页面会执行这段恶意代码
  3. <script>alert('xss 攻击')</script>

基于DOM

基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击:

  1. <h2>XSS: </h2>
  2. <input type="text" id="input">
  3. <button id="btn">Submit</button>
  4. <div id="div"></div>
  5. <script>
  6. const input = document.getElementById('input');
  7. const btn = document.getElementById('btn');
  8. const div = document.getElementById('div');
  9. let val;
  10. input.addEventListener('change', (e) => {
  11. val = e.target.value;
  12. }, false);
  13. btn.addEventListener('click', () => {
  14. div.innerHTML = `<a href=${val}>testLink</a>`
  15. }, false);
  16. </script>
  17. 复制代码

点击 Submit 按钮后,会在当前页面插入一个链接其地址为用户的输入内容
如果用户在输入时构造了如下内容

'' onclick=alert(/xss/)

用户提交之后,页面代码就变成了: testLink

此时,用户点击生成的链接,就会执行对应的脚本。

防范

HttpOnly 防止劫取 Cookie

HttpOnly 防止劫取 Cookie:HttpOnly 最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的Javascript 访问带有 HttpOnly 属性的Cookie。上文有说到,攻击者可以通过注入恶意脚本获取用户的 Cookie 信息。通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持攻击。

输入检查: 检查、过滤、转义

转义的原理:
比如用户输入 是把这段文字当做字符串内容的,而不是一段可执行的代码;
而直接把这段代码给浏览器的话,浏览器会以为是可执行代码,所以就是转义,让浏览器 ‘<’: ‘<’,
< 转义为 ‘<浏览器在解析时会自然把’<解析为< 从而恢复成原本str的样子,又不会被当做执行代码;

输入检查:不要相信用户的任何输入。 对于用户的任何输入要进行检查、过滤和转义。
建立可信任的字符和 HTML 标签白名单,对于不在白名单之列的字符或者标签进行过滤或编码

在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <,> 等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如 <,>,script,防止 XSS 攻击:

// vuejs 中的 decodingMap
// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&amp;': '&',
  '&#10;': '\n'
}

输出检查:用户的输入会存在问题,服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中