React元素$$typeof属性
理解React使用$$typeof防止XSS攻击的机制
问题
为什么 React 元素有一个 $$typeof 属性?它的作用是什么?
解答
React 元素的结构
React 元素本质上是普通的 JavaScript 对象:
// JSX
const element = <div className="container">Hello</div>;
// 编译后的对象结构
const element = {
$$typeof: Symbol.for('react.element'),
type: 'div',
props: {
className: 'container',
children: 'Hello'
},
key: null,
ref: null
};
为什么需要 $$typeof
$$typeof 是 React 用来防止 XSS 攻击的安全机制。
假设你的应用允许用户存储 JSON 数据,并在服务端渲染时使用:
// 服务端从数据库读取用户数据
const userData = JSON.parse(dataFromDatabase);
// 直接渲染用户数据
function UserProfile({ user }) {
return <div>{user.bio}</div>;
}
如果没有 $$typeof 保护,攻击者可以存储恶意的 JSON:
// 攻击者存储的恶意数据
{
"bio": {
"type": "div",
"props": {
"dangerouslySetInnerHTML": {
"__html": "<script>alert('XSS')</script>"
}
}
}
}
Symbol 的作用
React 使用 Symbol.for('react.element') 作为 $$typeof 的值:
// React 源码中的定义
const REACT_ELEMENT_TYPE = Symbol.for('react.element');
function createElement(type, props, ...children) {
return {
$$typeof: REACT_ELEMENT_TYPE, // 标记为合法的 React 元素
type,
props: {
...props,
children
},
key: null,
ref: null
};
}
关键点:JSON 不支持 Symbol 类型
// Symbol 无法被 JSON 序列化
JSON.stringify({ $$typeof: Symbol.for('react.element') });
// 输出: "{}"
// 攻击者无法通过 JSON 注入伪造 $$typeof
const malicious = JSON.parse('{"$$typeof": "Symbol.for(react.element)"}');
// malicious.$$typeof 是字符串,不是 Symbol
React 的验证逻辑
React 在渲染时会检查 $$typeof:
// 简化的验证逻辑
function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === Symbol.for('react.element')
);
}
// 渲染时
function render(element) {
if (!isValidElement(element)) {
// 当作普通文本处理,自动转义
return escapeHtml(String(element));
}
// 作为 React 元素渲染
return renderElement(element);
}
浏览器兼容处理
对于不支持 Symbol 的旧浏览器,React 使用特殊数字:
const REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol.for
? Symbol.for('react.element')
: 0xeac7; // 看起来像 "React"
关键点
$$typeof是 React 元素的身份标识,用于区分合法元素和普通对象- 使用 Symbol 类型是因为 JSON 无法序列化 Symbol,防止服务端 JSON 注入攻击
- React 渲染前会验证
$$typeof,不合法的对象会被当作文本处理并转义 - 这是一种纵深防御策略,即使存在其他漏洞也能提供保护
- 不支持 Symbol 的环境使用
0xeac7作为降级方案
目录