在不进行清理的情况下,使用危险地将SetInnerHTML呈现给页面的服务器响应是否明智?

问题描述

我认为使用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>标签的数据,以绝对消除任何有害的可能性。为此,您可以使用document.createElement()投射HTML,然后删除所有<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);