React中是如何通过$$typeof防止XSS攻击的

4,256 阅读1分钟

看到好多文章说React可以防止XSS攻击,一直不明白其中原理,以为React做的就是进行文本转义,知道React元素又个$$typeof属性。最近好好研究了下,基本明白了其中原理。
官方CHANAGELOG和issue里都有提及。

首先什么是XSS攻击

这里直接引用下大神文章内容
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。
XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

React中做了什么

  • 基础的对用户的输入内容进行转义,它会在运行时动态创建DOM节点然后填入文本内容
  • 元素$$typeof属性
转义很好理解,这里重点说下$$typeof属性

虚拟DOM

React的核心机制:虚拟DOM。我们平时写的JSX,实际上babel会帮我们转成React.createElement,用来生成虚拟DOM树。以下是源码部分。

createElement方法中处理了children、key、ref、defaultProps等,调用ReactElement

再来看,调用ReactElement源码中返回了虚拟dom节点对象,拥有typeoftypekeyrefpropsowner属性,这里看一眼typeof、type、key、ref、props、_owner属性,这里看一眼typeof是什么。

$$typeof是个symbol值,不支持symbol的环境会是一个特殊数字,为什么是这个数字?据说因为 0xeac7看起来有点像 “React”。。。。特意查了官方issue地址

我们打印个React元素看看到底长什么样:

和上面源码中ReactElement属性一致。

没有$$typeof属性如何XSS

上面说到,JSX实际是React.createElement的语法糖,如果跳过这个方法,服务端返回了一个结构话的JSON,并且和ReactElement元素结构一致,同时搭配dangerouslySetInnerHTML这个api,跳过react的转义,React是不是也会将这个渲染到真实dom中去,我们试一下:

如上代码,通过对象构造和ReactElement一样的结构,但是这里把$$typeof属性也加进去,点击button,页面就会渲染这个对象:

可以看到,XSS攻击成功了。试一下去掉$$typeof属性会怎么样:

直接报错了,提示object不是有效的react元素。如果这里没有$$typeof属性,一个普通的js对象就可以触发XSS攻击,但是js中symbol值在stringify时是会被忽略的,也无法在前后端间传输,所以请求返回值肯定不会拿到REACT_ELEMENT_TYPE。再show下React渲染判断的源码

源码里就是判断$$typeof是否等于REACT_ELEMENT_TYPE来判断是否是React节点。

至此,我们明白了React中$$typeof的作用,后端返回的JSON不支持Symbol类型。即使服务端存在用JSON作为文本返回安全漏洞,JSON里也不包含 Symbol.for('react.element'),React也不会渲染处理。 附上本文源码,本仓库会持续更新React中一些疑问或者新知识点,欢迎关注。