详解React 如何防止 XSS 攻击论$$typeof 的作用

 更新时间:2022年07月25日 09:34:51   作者:​ runnerdancer​  
这篇文章主要介绍了详解React 如何防止 XSS 攻击论$$typeof 的作用,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

JSX

先来简单复习一下 JSX 的基础知识。JSX 是React.createElement的语法糖

<div id="container">hello</div>

经过 babel 编译后:

React.createElement(
  "div" /* type */,
  { id: "container" } /* props */,
  "hello" /* children */
);

React.createElement最终返回的结果就是一个对象,如下:

{
  type: 'div',
  props: {
    id: 'container',
    children: 'hello',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'),
}

这就是一个 React element 对象。

我们甚至可以在代码中直接写 React element 对象,React 照样能正常渲染我们的内容:

render() {
  return (
    <div>
      {{
        $$typeof: Symbol.for('react.element'),
        props: {
          dangerouslySetInnerHTML: {
            __html: '<img src="x" onerror="alert(1)">'
          },
        },
        ref: null,
        type: "div",
      }}
    </div>
  );
}

可以复制这段代码本地运行一下,可以发现浏览器弹出一个弹窗,并且img已经插入了 dom 中。

这里,$$typeof 的作用是啥?为什么使用 Symbol() 作为值?

在了解之前,我们先来简单看下 XSS 攻击

XSS 攻击

我们经常需要构造 HTML 字符串并插入到 DOM 中,比如:

const messageEl = document.getElementById("message");
var message = "hello world";
messageEl.innerHTML = "<p>" + message + "</p>";

页面正常显示。但是如果我们插入一些恶意代码,比如:

const messageEl = document.getElementById("message");
var message = '<img src onerror="alert(1)">';
messageEl.innerHTML = "<p>" + message + "</p>";

此时页面就会弹出一个弹窗,弹窗内容显示为 1

因此,直接使用 innerHTML 插入文本内容,存在 XSS 攻击的风险

防止 XSS 攻击的方法

为了解决类似的 XSS 攻击方法,我们可以使用一些安全的 API 添加文本内容,比如:

  • 使用 document.createTextNode('hello world') 插入文本节点。
  • 或者使用 textContent 而不是 innerHTML 设置文本内容。
  • 对于一些特殊字符,比如 <>,我们可以进行转义,将其转换为 &#60; 以及 &#62;
  • 对于富文本内容,我们可以设置黑名单,过滤一些属性,比如 onerror 等。

React 对于文本节点的处理

React 使用 createTextNode 或者 textContent 设置文本内容。

对于下面的代码:

render() {
  const { count } = this.state
  return (
    <div onClick={() => this.setState({ count: count + 1})}>
      {count}
    </div>
  );
}

React 在渲染过程中会调用setTextContent方法为div节点设置内容,其中,第一次渲染时,直接设置div节点的textContent,第二次或者第二次以后的更新渲染,由于第一次设置了textContent,因此divfirstChild值存在,是个文本节点。此时直接更新这个文本节点的nodeValue即可

var setTextContent = function (node, text) {
  if (text) {
    var firstChild = node.firstChild;
    // 如果当前node节点已经设置过textContent,则firstChild不为空,是个文本节点TEXT_NODE
    if (
      firstChild &&
      firstChild === node.lastChild &&
      firstChild.nodeType === TEXT_NODE
    ) {
      firstChild.nodeValue = text;
      return;
    }
  }
  // 第一次渲染,直接设置textContent
  node.textContent = text;
};

综上,对于普通的文本节点来说,由于 React 是采用 textContent 或者 createTextNode 的方式添加的,因此是不会存在 XSS 攻击的,即使上面示例中,count 的值为 '<img src="x" onerror="alert(1)">'也不会有被攻击的风险

dangerouslySetInnerHTML 处理富文本节点

有时候我们确实需要显示富文本的内容,React 提供了dangerouslySetInnerHTML方便我们显式的插入富文本内容

render() {
  return (
    <div
      id="dangerous"
      dangerouslySetInnerHTML={{ __html: '<img src="x" onerror="alert(1)">' }}
    >
    </div>
  );
}

React 在为 DOM 更新属性时,会判断属性的key是不是dangerouslySetInnerHTML,如果是的话,调用setInnerHTML 方法直接给 dom 的innerHTML属性设置文本内容

function setInitialDOMProperties(
  tag,
  domElement,
  rootContainerElement,
  nextProps
) {
  for (var propKey in nextProps) {
    if (propKey === "dangerouslySetInnerHTML") {
      var nextHtml = nextProp ? nextProp.__html : undefined;
      if (nextHtml != null) {
        setInnerHTML(domElement, nextHtml);
      }
    }
  }
}
var setInnerHTML = function (node, html) {
  node.innerHTML = html;
};

可以看出,React 在处理富文本时,也仅仅是简单的设置 DOM 的innerHTML属性来实现的。

对于富文本潜在的安全风险,交由开发者自行把控。

$$typeof 的作用

render() {
  const { text } = this.state
  return (
    <div>
      {text}
    </div>
  );
}

假设这个text是从后端返回来的,同时后端允许用户存储 JSON 对象,如果用户传入下面这样的一个类似 React element 的对象:

{
  type: "div",
  props: {
    dangerouslySetInnerHTML: {
      __html: '<img src="x" onerror="alert(1)">'
    },
  },
  ref: null
}

别忘了前面说过,我们在 JSX 中直接插入 React element 对象也是能够正常渲染的。

在这种情况下,在React0.13版本时,这是一个潜在的XSS攻击,这个漏洞源于服务端。如果攻击者恶意伪造一个类似 React element 对象的数据返回给前端,React 就会执行恶意代码。但是 React 可以采取措施预防这种攻击。

React0.14版本开始,React 为每个 element 都添加了一个Symbol标志:

{
  $$typeof: Symbol.for('react.element'),
  props: {
    id: 'container'
  },
  ref: null,
  type: "div",
}

这个行得通,是因为 JSON 不支持Symbol。因此即使是服务端有风险漏洞并且返回一个 JSON,这个 JSON 也不会包含Symbol.for('react.element').,在 Reconcile 阶段,React 会检查element.$$typeof标志是否合法。不合法的话直接报错,React 不能接受对象作为 children

专门使用 Symbol.for() 的好处是, Symbols 在 iframe 和 worker 等环境之间是全局的。因此,即使在更奇特的条件下,Symbols 也能在不同的应用程序之间传递受信任的元素。同样,即使页面上有多个 React 副本,它们仍然可以“同意”有效的 $$typeof 值

如果浏览器不支持Symbols,React 使用0xeac7代替

{
  $$typeof: '0xeac7',
}

到此这篇关于详解React 如何防止 XSS 攻击论$$typeof 的作用的文章就介绍到这了,更多相关React $$typeof 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React实现全局组件的Toast轻提示效果

    React实现全局组件的Toast轻提示效果

    这篇文章主要介绍了React实现全局组件的Toast轻提示效果,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • React组件通信的实现示例

    React组件通信的实现示例

    在React中,组件通信是一个重要的概念,它允许不同组件之间进行数据传递和交互,本文主要介绍了React组件通信的实现示例,感兴趣的可以了解一下
    2023-11-11
  • React钩子函数之useDeferredValue的基本使用示例详解

    React钩子函数之useDeferredValue的基本使用示例详解

    useDeferredValue是React 18中非常有用的一个钩子函数,它可以帮助我们优化渲染性能,并让UI更加流畅,如果你还没有尝试过它,不妨在你的下一个React项目中试一试,这篇文章主要介绍了React钩子函数之useDeferredValue的基本使用,需要的朋友可以参考下
    2023-08-08
  • react中value与defaultValue的区别及说明

    react中value与defaultValue的区别及说明

    这篇文章主要介绍了react中value与defaultValue的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • React Hook中的useEffecfa函数的使用小结

    React Hook中的useEffecfa函数的使用小结

    React 会在组件更新和卸载的时候执行清除操作, 将上一次的监听取消掉, 只留下当前的监听,这篇文章主要介绍了React Hook useEffecfa函数的使用细节详解,需要的朋友可以参考下
    2022-11-11
  • react函数组件useState异步,数据不能及时获取到的问题

    react函数组件useState异步,数据不能及时获取到的问题

    这篇文章主要介绍了react函数组件useState异步,数据不能及时获取到的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • React 封装自定义组件的操作方法

    React 封装自定义组件的操作方法

    React中自定义组件的重要性在于它们提供了代码重用、降低耦合性、提升可维护性、更好的团队协作、灵活性和易于测试和调试等好处,从而提高了开发效率和质量,这篇文章主要介绍了React 封装自定义组件,需要的朋友可以参考下
    2023-12-12
  • React Native基础入门之调试React Native应用的一小步

    React Native基础入门之调试React Native应用的一小步

    这篇文章主要给大家介绍了关于React Native基础入门之调试React Native应用的相关资料,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • React.js入门实例教程之创建hello world 的5种方式

    React.js入门实例教程之创建hello world 的5种方式

    React 是近期非常热门的一个前端开发框架。应用非常广泛,接下来通过本文给大家介绍React.js入门实例教程之创建hello world 的5种方式 ,需要的朋友参考下吧
    2016-05-05
  • 基于PixiJS实现react图标旋转动效

    基于PixiJS实现react图标旋转动效

    PixiJS是一个开源的基于web的渲染系统,为游戏、数据可视化和其他图形密集型项目提供了极快的性能,这篇文章主要介绍了用PixiJS实现react图标旋转动效,需要的朋友可以参考下
    2022-05-05

最新评论