【为什么react元素有一个$$typeof属性】

简介: 【为什么react元素有一个$$typeof属性】

文章标签: react.js javascript 前端

版权

举个例子:例如我们的jsx长这个样子:

<Button type="primary">点击</Button>

但是实际上,在经过babel后,它会变成下面这段代码:

React.createElement(
  /* type */ 'Button',
  /* props */ { type: 'primary' },
  /* children */ '点击'

之后,这个函数执行结果会返回一个对象,这个对象我们称为React Element。它是一个用来描述我们将要渲染的页面结构的一个不可变对象。想了解更多与React Component,Elements和Inastances的可以点击这里。

// React Element
{
  type: 'Button',
  props: {
    type: 'primary',
    children: '点击',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'), // 为什么有这个东西
}

对于React开发者来说,上面这些属性大部分都是比较常见的。可是为什么混进了一个奇怪的$$typeof??它是干嘛的呢?它的值为什么是一个Symbol呢?

这个属性的引入,其实要从一个安全漏洞说起。

假如我们要显示一个变量,如果你使用纯js来写的话,可能是这样:

const messageEl = document.getElementById('message');
messageEl.innerHTML = `<div>${message}</div>`;

这一段代码,对于熟悉或者了解过XSS攻击的人来说,一看就知道会有问题,存在着XSS攻击。如果message是用户可以控制的变量(比如说是用户输入的评论)的话,那么用户就可以进行攻击了。比如用户可以构造下面的代码来进行攻击:

message = '<img onerror="alert(2)" src="" />';

这样页面一加载到这段代码,就会弹出一个alert框。

如果我们明确知道,我们只想单纯的渲染文本,不想把它当成html来渲染的话,那么我们可以通过textContent来避免这个问题。

const messageEl = document.getElementById('message');
messageEl.textContent = `<div>${message}</div>`;

而对于React而言的话,想要实现相同的效果,只需要:

<div>{message}</div>

即使message里面含有img、script类似的标签,它们最终也不会以实际上的标签显示。React会对渲染的内容进行转译,比如说上面的攻击代码会被转译为:

message = '<img onerror="alert(2)" src=""/>';
// 转译为
message = '&lt;img onerror="alert(2)" src=""/&gt;'

因此,这样就可以避免大部分场景下的XSS攻击了。

当然,React也提供了另一种方式来将用户输入的内容当成html来渲染:

<div dangerouslySetInnerHTML={{ __html: message }}></div>

前面说了这么多,那么跟$$typeof又有什么关系呢?别急,重点来了。

对于下面这种写法,我们一般都知道,message可以传基本类型、自定义组件和jsx片段。

<div>{message}</div>

可是,其实我们还可以直接传React Element。比如,我们可以直接这样写

class App extends React.Component {
  render() {
    const message = {
      type: "div",
      props: {
        dangerouslySetInnerHTML: {
          __html: `<h1>Arbitrary HTML</h1>
            <img onerror="alert(2)" src="" />
            <a href='http://danlec.com'>link</a>`
        }
      },
      key: null,
      ref: null,
      $$typeof: Symbol.for("react.element")
    };
    return <>{message}</>;
  }
}

这样在运行的时候,就会弹出一个alert框了。查看demo。那么,这样会有什么风险呢?

考虑一个场景,比如一个博客网站的评论信息message是由用户提供的,并且支持传入JSON。那么如果用户直接将上文的message发送给后台保存。之后,通过下面这种方式展示的话,用户就可以进行XSS攻击了。

<div>{message}</div>

假设如果没有$$typeof属性的话,这种攻击确实可行。因为其他的属性都是可序列化的。

const message = {
  type: "div",
  props: {
    dangerouslySetInnerHTML: {
      __html: `<h1>Arbitrary HTML</h1>
<img onerror="alert(2)" src="" />
<a href='http://danlec.com'>link</a>`
    }
  },
  key: null,
  ref: null,
};
JSON.stringify(message);

事实上,React 0.13当时就存在着这个漏洞。之后,React 0.14就修复了这个问题,修复方式就是通过引入$$typeof属性,并且用Symbol来作为它的值。

// 引入 $$typeof
const message = {
  type: "div",
  props: {
    dangerouslySetInnerHTML: {
      __html: `<h1>Arbitrary HTML</h1>
<img onerror="alert(2)" src="" />
<a href='http://danlec.com'>link</a>`
    }
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for("react.element")
};
JSON.stringify(message); // Symbol无法被序列化

这是一个有效的方法,因为JSON是不支持Symbol类型的。所以,即使用户提交了如上的message信息,到最后服务端也不会保存$$typeof属性。而在渲染的时候,React 会检测是否有$$typeof属性。如果没有这个属性,则拒绝处理该元素。

那么如果浏览器不支持Symbol怎么办?

是的,那这种保护方案就没用了。React 依然会加上$$typeof字段,并且将其值设置为0xeac7。(为什么是这个数字呢,因为这个数字看起来有点像React)。

想查看具体的攻击流程,可以查看这篇博客。


相关文章
|
7月前
|
前端开发 JavaScript API
第八章 react组件实例中三大属性之ref
第八章 react组件实例中三大属性之ref
|
2月前
|
前端开发 JavaScript 容器
React 元素渲染
10月更文挑战第7天
29 1
|
3月前
|
前端开发
React属性之context属性
React中的Context属性用于跨组件传递数据,通过Provider和Consumer组件实现数据的提供和消费。
37 3
|
2月前
|
前端开发 JavaScript API
React将组件作为属性传递的最佳实践
本文探讨了在React中将组件作为属性传递的三种常见方式:作为元素传递、作为组件传递、作为函数传递。通过构建带图标的按钮组件,对比分析了每种方式的优缺点,最终推荐将组件作为函数传递,因为它提供了更好的可控性、灵活性和可扩展性。
45 0
|
3月前
|
前端开发 JavaScript
React 中的 props 属性传递技巧
【9月更文挑战第6天】本文详细介绍了React中`props`的基本用法,包括传递基本数据类型、对象和数组。文章通过多个代码示例展示了如何正确使用`props`,并探讨了常见的问题及解决方法,如`props`不可变性、默认值设置及类型检查等。正确掌握这些技巧有助于提升编程效率,编写出更健壮的代码。
78 13
|
3月前
|
前端开发
React使用hooks遇到的坑_state中的某几个属性数据变成了空字符
本文讨论了在React使用hooks时遇到的一个问题:state中的某些属性数据变成了空字符。作者通过在修改函数中重新解构赋值来获取最新的state值,解决了因数据更新不及时导致的问题。
85 0
|
4月前
|
JavaScript 前端开发 容器
React组件属性refs(七)
【8月更文挑战第14天】React组件属性refs(七)
53 0
React组件属性refs(七)
|
4月前
|
前端开发 JavaScript
|
4月前
|
前端开发 JavaScript
React 组件和 React 元素有什么区别?
【8月更文挑战第31天】
38 0
|
7月前
|
JavaScript
react+typescript通过window.xxx挂载属性报错的解决方案
react+typescript通过window.xxx挂载属性报错的解决方案
245 0