在没有清理的情况下使用 dangerouslySetInnerHTML 向页面呈现服务器响应是否明智?

Is it wise to render a server response using dangerouslySetInnerHTML to the page without sanitization?

我认为使用 React 的 dangerouslySetInnerHTML 属性 将从服务器获取的标记放置在页面上是很常见的,即

const SomeComponent = () => {
  const [markup, setMarkup] = useState(null)

  useEffect(() => { 
    const resp = await fetch(...)
    setMarkup(resp.content)
  })

  return <div dangerouslySetInnerHTML={{ __hmtl: markup }} />
}

如果这是一个不同的场景,并且标记来自页面上的表单,这显然会带来风险,因为您不能信任在表单上输入的数据,而且我们不会在这里进行任何清理。

但是,我们将从服务器返回的数据放在页面上,因此可能存在某种程度的信任。对服务器的调用发生在代码中,大概我们知道我们正在调用的API。

但是,即使我们信任服务器,也认为来自信任服务器的数据实际上是不明智的吗?坏人能否在数据返回之前干预线路?

完全信任的问题dangerouslySetInnerHTML

dangerouslySetInnerHTML 采取最低限度的预防措施有很多原因。由于浏览器的逻辑是在其他地方定义的,因此其他地方就会成为失败点。

  • 用于审查和修改您如何构建 HTML 代码逻辑的内部流程是否失败?这是否允许 XSS 攻击?
  • 是否有人忘记更新 SSL 证书?域名注册?有人已经对它进行了网络抢注,现在您的应用程序使用来自被劫持域的 API?
  • DNS 名称服务器是否被黑客入侵,将您的 API 域指向不同的服务器?路由器或任何中间网络设备怎么样?
  • 您自己的服务器是否遭到黑客攻击?可能性最小(眨眼),但也有可能。

安全使用dangerouslySetInnerHTML

但是,有时您需要 dangerouslySetInnerHTML,因为这是最简单的解决方案。例如,存储、保存和检索标记非常容易如粗体、斜体等,通过将其保存并存储为原始 HTML.

至少,请在发送给用户之前清理任何<script>标签的数据,以绝对消除任何有害的可能性。您可以通过将 HTML 转换为 document.createElement(),然后删除任何 <script> 标记节点来实现。

有趣的事实:当您创建带有 <script> 标签的元素时,SO 的演示不喜欢它!下面的代码片段不会 运行,但它确实适用于:Full Working Demo at JSBin.com.

var el = document.createElement( 'html' );
el.innerHTML = "<p>Valid paragraph.</p><p>Another valid paragraph.</p><script>Dangerous scripting!!!</script><p>Last final paragraph.</p>";

var scripts = el.getElementsByTagName( 'script' );

for(var i = 0; i < scripts.length; i++) {
  var script = scripts[i];
  script.remove();
}

console.log(el.innerHTML);

document.getElementById('main').append(el);