问答题609/1593说说你对 dangerouslySetInnerHTML 的理解

难度:
2022-08-23 创建

参考答案:

本文介绍了在React应用程序中使用dangerouslySetInnerHTML 属性的原因,它相当于浏览器DOM中的innerHTML 属性。

什么是dangerouslySetInnerHTML

dangerouslySetInnerHTML 是一个属性,你可以在 React 应用程序中的 HTML 元素上使用,以编程方式设置其内容。你可以直接在元素上使用这个属性,而不是使用选择器来抓取HTML元素,然后设置其innerHTML

当使用dangerouslySetInnerHTML ,React也知道该特定元素的内容是动态的,对于该节点的子节点,它只是跳过与虚拟DOM的比较,以获得一些额外的性能。

正如该属性的名称所暗示的,使用它可能是危险的,因为它使你的代码容易受到跨站脚本(XSS)攻击。特别是当你从第三方来源获取数据或渲染用户提交的内容时,这就成为一个问题。

何时使用dangerouslySetInnerHTML

你需要设置DOM元素的HTML内容的一个用例是当你用来自富文本编辑器的数据填充一个<div> 。想象一下,你有一个网页,人们可以提交评论,你允许他们使用一个富文本编辑器。在这种情况下,富文本编辑器的输出很可能是带有标签的HTML,如<p>,<b>, 和<img>

考虑一下下面的代码片段,它将在不知道其中的<b> 标签的情况下渲染字符串--意味着输出的只是字符串本身,没有任何粗体字,就像这样:lorem ipsum

1const App = () => { 2 const data = 'lorem <b>ipsum</b>'; 3 4 return ( 5 <div> 6 {data} 7 </div> 8 ); 9} 10 11export default App;

但当使用dangerouslySetInnerHTML ,React就会意识到HTML标签,并正确渲染它们。这一次,输出将以粗体文本正确呈现(即loremipsum)。

1const App = () => { 2 const data = 'lorem <b>ipsum</b>'; 3 4 return ( 5 <div 6 dangerouslySetInnerHTML={{__html: data}} 7 /> 8 ); 9} 10 11export default App;

请注意,它应该是一个带有传递给__html 键的对象dangerouslySetInnerHTML 。除此之外,你使用dangerouslySetInnerHTML 属性的元素不应该有任何孩子,因此要使用<div> 元素作为自闭标签。

传递对象的要求只是另一种保障措施,以防止开发者在没有阅读文档和意识到潜在危险的情况下使用它。

使用时的消毒dangerouslySetInnerHTML

上面的例子在渲染时不会造成危险。然而,在某些情况下,HTML元素可能会执行一个脚本。

考虑一下下面的例子,一个JavaScript事件被附加到一个HTML元素上。虽然这些是无害的例子,但它们是概念的证明,表明一个HTML元素如何被利用来运行恶意脚本。

1const App = () => { 2 const data = `lorem <b onmouseover="alert('mouseover');">ipsum</b>`; 3 4 return ( 5 <div 6 dangerouslySetInnerHTML={{__html: data}} 7 /> 8 ); 9} 10 11export default App; 12 13 14const App = () => { 15 const data = `lorem ipsum <img src="" onerror="alert('message');" />`; 16 17 return ( 18 <div 19 dangerouslySetInnerHTML={{__html: data}} 20 /> 21 ); 22} 23 24export default App;

幸运的是,有针对HTML的净化工具,可以检测出HTML代码中潜在的恶意部分,然后输出一个干净安全的版本。最受欢迎的HTML净化工具是DOMPurify

让我们使用它的在线演示来对上述HTML代码进行消毒,看看它是如何检测并过滤掉代码中可能在执行时产生危险的部分的。

1Original 2lorem <b onmouseover="alert('mouseover');">ipsum</b> 3 4Sanitized 5lorem <b>ipsum</b>
1Original 2lorem ipsum <img src="" onerror="alert('message');" /> 3 4Sanitized 5lorem ipsum <img src="">

即使在我们信任数据来源的情况下,使用消毒剂也是很好的做法。在使用DOMPurify包的情况下,上面的一个例子会是这样的。

1import DOMPurify from 'dompurify' 2 3const App = () => { 4 const data = `lorem <b onmouseover="alert('mouseover');">ipsum</b>` 5 const sanitizedData = () => ({ 6 __html: DOMPurify.sanitize(data) 7 }) 8 9 return ( 10 <div 11 dangerouslySetInnerHTML={sanitizedData()} 12 /> 13 ); 14} 15 16export default App;

sanitizedData 函数返回一个带有__html 键的对象,它有一个从DOMPurify.sanitize 函数返回的值。

正如预期的那样,当我们将鼠标悬停在粗体字上时,并没有执行警报函数。

请注意,由于DOMPurify需要一个DOM树,而Node环境没有,你要么使用jsdom 包来创建一个window 对象,并用它来初始化DOMPurify ,要么单独使用isomorphic-dompurify 包来代替,它同时封装了DOMPurifyjsdom 包。

如果你喜欢第一种选择,你可以参考以下来自DOMPurify 的文档片段。

1const createDOMPurify = require('dompurify'); 2const { JSDOM } = require('jsdom'); 3 4const window = new JSDOM('').window; 5const DOMPurify = createDOMPurify(window); 6 7const clean = DOMPurify.sanitize(dirty);

结论

总之,dangerouslySetInnerHTML 只不过是React中innerHTML 的替代品,应该谨慎使用。虽然这个名字暗示了使用它的危险性,但通过使用一个完善的净化器采取必要的措施,确保代码是干净的,在React节点内呈现时不会运行意外的脚本。

最近更新时间:2024-08-10

赞赏支持

预览

题库维护不易,您的支持就是我们最大的动力!