共计 1732 个字符,预计需要花费 5 分钟才能阅读完成。
简评:debug 的时候看到 element 对象中有 $$typeof 这属性,于是查了一下这到底干嘛的。
我们知道,通过 JSX 创建一个 React Elements 时:
<marquee bgcolor=”#ffa7c4″>hi</marquee>
实际上调用的是 React.createElement 方法:
React.createElement(
/* type */ ‘marquee’,
/* props */ {bgcolor: ‘#ffa7c4’},
/* children */ ‘hi’
)
该方法会返回一个 React Element 对象,大概长这样:
{
type: ‘marquee’,
props: {
bgcolor: ‘#ffa7c4’,
children: ‘hi’,
},
key: null,
ref: null,
$$typeof: Symbol.for(‘react.element’), // ???? Who dis
}
这个 $$typeof 是什么?
各种前端框架出现之前,应用通常会构造 HTML 并将它们插入到 DOM 中,例如:
const messageEl = document.getElementById(‘message’);
messageEl.innerHTML = ‘<p>’ + message.text + ‘</p>’;
这代码一般能正常工作,除非你的 message.text 返回 ‘<img src onerror=”stealYourPassword()”>’ 这样的字符串。
为了防止这种情况,可以使用类似于 document.createTextNode() 或 textContent 仅处理 text 的 api。也可以通过预处理替换 < > 这类特殊的字符。但是这样还是有很多潜在的问题。所以现代前端框架像 React 会为字符串转义成文字内容:
<p>
{message.text}
</p>
就算 message.text 返回 <img> 也会当成字符串处理。要在 React 元素中呈现 HTML,则需要编写 dangerouslySetInnerHTML={{__html: message.text}},这有助于你在审查代码的时候重视它。
但是这样仍然无法完全禁止注入攻击,例如:<a href={user.website}>,注意 website 为 ‘javascript: stealYourPassword()’ 的情况。对用户输入使用 … 运算符也很危险 <div {…userData}>。
为什么需要 $$typeof
通过上面的介绍你可能已经猜到了,$$typeof 是为了防止 XSS 攻击。
前面介绍了,React 元素大概是这样的(没有包含 $$typeof 这个属性的情况):
{
type: ‘marquee’,
props: {
bgcolor: ‘#ffa7c4’,
children: ‘hi’,
},
key: null,
ref: null
}
试想这样一种情况,服务器允许用户储存任意的 JSON 数据。那么就可以手动构建 React Element 对象传入到元素中。例如:
// Server could have a hole that lets user store JSON
let expectedTextButGotJSON = {
type: ‘div’,
props: {
dangerouslySetInnerHTML: {
__html: ‘/* put your exploit here */’
},
},
// …
};
let message = {text: expectedTextButGotJSON};
// Dangerous in React 0.13
<p>
{message.text}
</p>
所以 React 0.14 使用 Symbol 标记每个 React 元素。
{
type: ‘marquee’,
props: {
bgcolor: ‘#ffa7c4’,
children: ‘hi’,
},
key: null,
ref: null,
$$typeof: Symbol.for(‘react.element’),
}
这样就可以规避这个问题,使用 Symbol 类型是因为 JSON 中无法传递 Symbol。React 会检查 element.$$typeof 然后拒绝处理非法的元素。
原文:Why Do React Elements Have a $$typeof Property?