使用angerlySetInnerHTML执行脚本

Chr*_*phe 1 reactjs dangerouslysetinnerhtml

我在几个地方读到,当我们使用 ReactdangerouslySetInnerHTML插入 html 片段时,脚本不会被执行。

不过我只是尝试插入这个:

<img src= "img.png" onload="alert('picture loaded')" alt="script test">  
Run Code Online (Sandbox Code Playgroud)

并且警报被触发了。

这并不让我感到惊讶(这就是我首先进行测试的原因),但我想更好地理解“脚本不执行”的含义。

我的问题:

  • 除了上面的示例之外,还有其他类型的脚本可以执行吗?
  • 有没有办法完全阻止脚本执行,包括像我的示例中嵌入 html 事件处理程序中的脚本?
  • 如果脚本标签定义了一个函数,该函数是否仍会被加载并可以在以后调用?
  • 我应该注意的任何其他行为吗?

dangerouslySetInnerHTML[编辑]我在函数组件中使用:

const htmlString = '<img src="img.png" onload="alert('picture loaded')" alt="script test">'
Run Code Online (Sandbox Code Playgroud)

在返回语句(JSX)中:

return <div dangerouslySetInnerHTML={{__html: htmlString}} />
Run Code Online (Sandbox Code Playgroud)

Cer*_*nce 5

“不会执行”的脚本是 script标签,例如<script>. 请参阅此处的示例:

const html = `
<script>console.log('this does NOT run');<\/script>
<img src onerror="console.log('but this will')">
`;
const App = () => {
  return <div dangerouslySetInnerHTML={{__html: html}} />;
};
ReactDOM.render(<App />, document.querySelector('.react'));
Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
Run Code Online (Sandbox Code Playgroud)

如果内联处理程序附加到的事件触发,则内联处理程序(不是<script>标签)可以运行。(上面,<img>具有src属性但没有有效路径的标记会引发错误,因此其onerror内联处理程序运行)

没有其他类别的脚本可以与 结合运行dangerouslySetInnerHTML(除非内联处理程序本身<script>通过其他方式注入 a,例如 with document.createElement('script'))。

有没有办法完全阻止脚本执行,包括像我的示例中嵌入 html 事件处理程序中的脚本?

您需要删除这些on-属性。如果on-删除所有属性,则任何可能触发的事件都不会导致意外的脚本运行。您可以首先通过 DOMParser 发送输入来清理输入:

const sanitize = (input) => {
  const doc = new DOMParser().parseFromString(input, 'text/html');
  for (const elm of doc.querySelectorAll('*')) {
    for (const attrib of elm.attributes) {
      if (attrib.name.startsWith('on')) {
        elm.removeAttribute(attrib.name);
      }
    }
  }
  return doc.body.innerHTML;
};

const html = `
<div>
  <script>console.log('this does NOT run');<\/script>
  <img src onerror="console.log('but this will')">
  more content
  <style type="text/css"> img {float: left; margin: 5px}</style> 
</div>
`;
const App = () => {
  return <div dangerouslySetInnerHTML={{__html: sanitize(html)}} />;
};
ReactDOM.render(<App />, document.querySelector('.react'));
Run Code Online (Sandbox Code Playgroud)
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
<img src="https://www.gravatar.com/avatar/f117a950c689d3d6ec459885a908166e?s=32&d=identicon&r=PG">
Run Code Online (Sandbox Code Playgroud)

如果脚本标签定义了一个函数,该函数是否仍会被加载并可以在以后调用?

不,因为<script>标签根本不会运行,所以它内部发生的任何事情都不会执行任何操作;内部定义的函数将不可见。为了发生这样的事情,您必须以某种方式故意重新加载新注入的<script>标签,以使其运行。