web前端 - React是如何预防XSS攻击的

154 阅读2分钟

一、背景

xss攻击是前端的日常会遇到的安全问题,本文概述react框架是如何预防xss攻击的,学习框架如何处理安全问题。

二、xss介绍

全称Cross Site Script,跨站脚本攻击。

XSS的定义:XSS是一种注入式攻击,通过向用户页面注入恶意脚本,用户浏览页面时运行脚本进攻攻击,如获取一些隐私信息, 例如cookie。

主要是利用:
1.利用了浏览器可以运行javascript的特点
2.html文档中可以包含可执行的脚本,html的部分内容可以在浏览器或服务器上动态生成
3.恶意代码常见的载体例如script标签,标签中的onclick, onerror这些可以直接填写代码的位置。

2.1 xss类型

反射型

恶意代码放在请求中,服务端读取后返回包含恶意代码的html

例如下面这请求中的query是一一段运行的js脚本
https://www.test.com?query=<script>alert(123)</script>

服务端获取query后直接原样拼接在html中返回<p>{{ 这里原样输出query的内容 }}<p>


这样上面恶意请求返回的html中就包含了<p><script>alert(123)</script></p>这样一段可执行的js, 可以利用起进行攻击,获取用户隐私信息等。

存储型

1.恶意代码放在请求中,服务端保存恶恶意代码至数据库
2.用户请求时,服务端从数据库里取出带恶意代码的字段,拼接在html中

例如下面这请求中的query是一一段运行的js脚本
https://www.test.com/save?query=<script>alert(123)</script>

服务端接受请求后将query保存至数据库

用户请求页面https://www.test.com/page

服务端读取数据库字段query后直接原样拼接在html中返回<p>{{ 这里原样输出query的内容 }}<p>

用户访问到的html中就包含了<p><script>alert(123)</script></p>这样一段可执行的js, 可以利用起进行攻击,获取用户隐私信息等。

DOM型

恶意代码放在请求中,客户端通过js动态插入到html中,比如使用eval()innerHTMLdocument.write()等不安全的API。

例如下面这请求中的query是一一段运行的js脚本
https://www.test.com/save?query=<script>alert(123)</script>

使用`eval()`、`innerHTML`、`document.write()`等不安全的API

const div = document.createElement('div');
div.innerHTML = decodeURIComponent(query)
document.body.appendChild(div);

2.2 xss分类

也可以通过危险代码是在客户端还是服务端生成的分为client型和server型

client型:DOM型

server型:反射型和存储型

三、react框架防御xss

react-client

1.进行DOM操作的时候避免直接拼接并插入DOM, 如.innerHTML、.outerHTML、document.write() ,而是使用.textContent改变文本内容、.setAttribute()改变标签属性。
2.danngerousSetInnerHtml可以直接原样插入html,慎用

react-server

对输出的html进行转义

function escapeHtml(string) {
  if (__DEV__) {
    checkHtmlStringCoercion(string);
  }
  const str = '' + string;
  const match = matchHtmlRegExp.exec(str);

  if (!match) {
    return str;
  }

  let escape;
  let html = '';
  let index;
  let lastIndex = 0;

  for (index = match.index; index < str.length; index++) {
    switch (str.charCodeAt(index)) {
      case 34: // "
        escape = '&quot;';
        break;
      case 38: // &
        escape = '&amp;';
        break;
      case 39: // '
        escape = '&#x27;'; // modified from escape-html; used to be '&#39'
        break;
      case 60: // <
        escape = '&lt;';
        break;
      case 62: // >
        escape = '&gt;';
        break;
      default:
        continue;
    }

    if (lastIndex !== index) {
      html += str.substring(lastIndex, index);
    }

    lastIndex = index + 1;
    html += escape;
  }

  return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
}