likes
comments
collection
share

手把手教你防御 XSS 攻击

作者站长头像
站长
· 阅读数 41

继上一篇文章介绍了如何实现 XSS 攻击之后,后面留下了伏笔,我们应该如何去防御文章介绍的 XSS 各种攻击呢?废话少说,直接上车!

手把手教你防御 XSS 攻击

您可以在线查看完整的示例源代码

攻击分类

XSS 攻击主要可以分为三类

  1. 存储型(持久型)(server端缺陷)
  2. 反射型(server 端缺陷)
  3. DOM 型(浏览器端缺陷)

接下来将从上面的攻击类型分别给出防御方案

反射型

手把手教你防御 XSS 攻击

为什么叫 ta 反射型,大概是因为

由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

而像上面这个是一个搜索的例子,用户需要点击下面这个链接

http://localhost:3000/search?search=<script>alert("反射型 XSS 攻击")</script>

这个反射型 XSS 攻击之所以能够成功的原因是后端未对用户提交的内容做校验,具体逻辑如下

/**
 * 基于用户搜索内容返回 /search 页面内容
 * @param {string} search 
 */
exports.generateSearchHTML = (search = "") => {
  return `
    <body>
      <form
        action="http://localhost:3000/search"
        method="GET"
        enctype="application/json"
      >
        搜索:<input class="search-input" name="search" type="text" />
        <br />
        搜索内容:${search}
        <br />
        <button class="confirm-button" type="submit">确认</button>
      </form>
    </body>
  `;
};

此类后端渲染直接拼接返回 HTML 字符串是主要原因,所以防御的主要手段就是对可能的恶意代码片段进行转义

啥是转义呢?为什么不直接把可能是恶意代码删掉就行了呢?

比如 <script>alert("反射型 XSS 攻击")</script> 里的 < > / 这几个字符,我删掉就行了

这样操作其实不太严谨,毕竟不是所有和 < > / 相关的内容都是恶意的,就比如我数学不好,我去搜索 5 < 7 的答案是多少,很合理吧?

手把手教你防御 XSS 攻击

所以过滤和删除是不行的,所以就要采用转义

转义(Escape)

有一些特别的字符被保留用于 HTML 中,这意味着浏览器会将这些字符解析为 HTML 代码。例如,如果你使用小于号(<),浏览器会将其后的文本解析为一个 tag

HTML 有一些特殊字符,就比如 < > / 这种字符,如果你想要在浏览器展示,想要 HTML 能够正常渲染,可以采用浏览器提供的实体(Entity

比如说

<span>123&amp;</span>

实际效果就是 123&

这个转义的意思和正则表达式的转义应该是差不多的

下面这个是文章使用到的转义字符的对照表

字符十进制转义字符
"&#34;&quot;
&&#38;&amp;
<&#60;&lt;
&#62;&gt;
不断开空格(non-breaking space)&#160;&nbsp;

更详细的对照表请参考下面的两个表

  1. HTML转义字符常用对照表 - OSCHINA
  2. 字符实体的官方列表 - 这个格式乱乱的,建议看上面这个

防御实操

回到反射型 XSS 攻击的防御,可以在 Server 端采用转义的方式解决,代码如下

// ...
exports.generateSearchHTML = (search = "") => {
  /**
   * 转义字符串
   * @param {string} originStr
   * @returns
   */
  const escape = (originStr) => {
    let str = originStr;
    str = str.replace(/</g, "&lt;");
    str = str.replace(/>/g, "&gt;");
    str = str.replace(/"/g, "&quot;");
    str = str.replace(/'/g, "&#x27;");
    str = str.replace(/\//g, "&#x2F;");
    return str;
  };

  return `
    ...
        搜索内容:${search}
    ...
  `;
};

我的正则应该还是非常 66666

手把手教你防御 XSS 攻击

效果如下

手把手教你防御 XSS 攻击

直接将攻击者的恶意代码渲染,并且不会执行,看看这次生成的 HTML

<body>
  <form
    action="http://localhost:3000/v2/search"
    method="GET"
    enctype="application/json"
  >
    搜索:<input class="search-input" name="search" type="text" />
    <br />
    搜索内容:&lt;script&gt;alert(&quot;反射型 XSS
    攻击&quot;)&lt;&#x2F;script&gt;
    <br />
    <button class="confirm-button" type="submit">确认</button>
  </form>
</body>

特殊字符都被转义了,恶意代码不会被执行

存储型和 DOM 型

如果你看过这篇我的前篇文章就知道,其实所谓的 XSS 攻击的三种分类都是一样的,对前端来说就是,获取变量然后渲染,XSS 在其中就是

  1. 提交恶意代码
  2. 浏览器执行恶意代码

XSS 攻击的处理和场景辨别是一个需要经验的知识,文章不可能介绍的了全部,尽可能的防御是对需要转义的场景可以采用受业界广泛采用的库,比如 leizongmin/js-xss

npm install xss

这样库出问题的时候就大家都出问题了,法不责众,哈哈哈哈哈哈哈哈哈

但是对于 XSS 攻击的防御还有最后一个需要介绍的

转义时机

XSS 攻击步骤

  1. 提交恶意代码
  2. 浏览器执行恶意代码

前端

首先前端转义再提交后端不靠谱,攻击者只要模拟发起请求,绕过前端,后端请求处理比较靠谱

后端

对于反射型来说,不需要存入数据库,而且需要拼接字符串,但对于存储型 XSS 攻击的入库内容就不太一样,比如下面这个场景

用户提交了一个评论内容,但是如果这个获取评论的 GET 请求 iOS/Andorid/Web 都需要展示呢?

比如内容在 5 < 7

如果在存入数据库时转义,那么数据库实际存储内容就是 5 &lt; 7

那么 iOS/Andorid 端请求时并展示时就是 5 &lt; 7

而对于 Web 来说,如果有用到这个请求的某个页面有 SEO 需求做了 SSR 处理,则拼接 HTML 字符串可以正常展示,而对于有用到这个请求的某个页面是使用 Ajax 来展示,则也会显示 5 &lt; 7

具体如下图

手把手教你防御 XSS 攻击

实际操作中可以

  1. 恶意代码存储进库,保证浏览器不执行就行了,拼接 HTML 处理即可
  2. 先过滤存储,在实际多端 GET 请求时再反转义一次,比如

iOS/Android 由 5 &lt; 7 转义至 5 < 7

参考资料

转载自:https://juejin.cn/post/7177015789382271035
评论
请登录