#
背景
在 React 项目中常会遇到渲染 HTML 内容的情况。可以利用 react 的 dangerouslySetInnerHTML 属性,完成基础开发。
示例:
1 |
|
不足
就作者目前查阅的资料和实践结果,上文提到的基础方案有一些不足。
以非虚拟 DOM 的方式渲染节点
React 对虚拟 DOM 设计了优化的算法(主要依赖 data-reactid
),放弃走虚拟 DOM 的渲染等同于放弃这些优化。
而 dangerouslySetInnerHTML
的渲染方式类似于原生 JS 的 HTML 渲染,显然放弃了节点优化:
1 |
|
实验下来,只有 dangerouslySetInnerHTML
所在的元素上带有 data-reactid
,而子元素都没有。
可以从 React 源码中证实:
1 | // ReactDOMComponent.js 部分源码: |
XSS 攻击
关于 XSS 的话题有点大,作者仅以自己的实验说明:
1 |
|
在基础方案下,input 元素完美渲染,点击正常,如果是恶意数据源,很容易造成严重后果。因此过滤必不可少。
解决思路
1.弃用 dangerouslySetInnerHTML,把文本 HTML 内容转化为 React-DOM 对象。
从 React
0.x 过来的小伙伴应该还没忘记没有 JSX 的时代,手写 React DOM 对象的开发方式。就算到了如今 JSX 也是先转换成 React DOM 对象再进行后面的渲染。
把 HTML 翻译成对象数组目前已有成熟的方案,htmlparse2 是个不错的选择。
不过 htmlparse2
生成的对象跟 React 特有的 DOM 对象还有一定距离,需要做进一步的转换,开源库 react-html-parser
这里做了不错的示范。
1 | //使用 react-html-parser 后: |
2.过滤高危元素
防止 XSS 攻击的主要手段之一就是过滤危险标签,例如 input
这种类型的元素则是重点「嫌疑人」。基于上面提到的对象转化,做到过滤并不难。安全等级和体验的平衡,取决于我们对转化后的对象的细致过滤。比如发现 tag 类型是 input 时一棍子打死,比如把有 onclick 的元素全部干掉。对于 XSS,最安全的态度是「永远不要相信用户输入的数据」。
1 | //使用 react-html-parser 后: |
小结
已上是作者在实践过程中遇到的问题,问题恐怕不止于此,但仅两点足以让我放弃直接使用 dangerouslySetInnerHTML
。这也正是 react 官方所提倡的做法,毕竟,这个属性的设计初衷就是要让开发者体会到「dangerous」。所以,再见吧,dangerouslySetInnerHTML
~