繁体   English   中英

尝试通过 DOM 修改 HTML 内容时如何避免 XSS?

[英]How to avoid XSS when trying to modify HTML content via DOM?

编号: https://github.com/aleen42/PersonalWiki/blob/docs/qa/avoid_xss_when_trying_to_modify_content_via_dom.md

在大多数情况下,当我们想要操作字符串内容时,我们可能更愿意将其挂载到 DOM 树上并进行操作,而不是使用复杂的正则表达式替换,例如:

  • 找到一些要删除的节点
  • 找到一些要删除的类
  • 修改内容。

但是,当外部内容包含一些意外脚本时,这种方法总是会导致 XSS 问题,例如以下代码段:

const unsafe = '<img src=x onerror=alert(1)>';
$('<div>').html(unsafe).find('img').remove(); // leads to a XSS

const div = document.createElement('div');
div.innerHTML = unsafe; // leads to XSS
const img = div.querySelector('img');
img && img.parentNode.removeChild(img);

那么我们该如何解决这个问题呢?

回滚到复杂的正则表达式? 为了避免这个问题,大多数开发人员可能会使用一些库,如js-xssDOMPurify来为他们处理复杂的正则表达式。 然而,这些库有时过于严格,无法删除一些安全标签,除非您可以设置正确的配置。

在这里,我只想澄清另一种简单的方法,我们可以依靠 iframe 沙盒技术,它还可以禁止浏览器访问来自加载图像等内容的资源:

const unsafe = '<img src=x onerror=alert(1)>';
const $frame = $('<iframe>').appendTo('body'), $sandboxDoc = $frame.contents();
$frame.remove();

// scripts have been blocked
// the document won't also access the image which throws us a 404
$('<div>', $sandboxDoc).html(unsafe).find('img').remove(); 

// by pure javascript
const frame = document.createElement('iframe');
document.body.appendChild(frame);
const sandboxDoc = frame.contentDocument;
frame.parentNode.removeChild(frame);
const div = sandboxDoc.createElement('div');
div.innerHTML = unsafe;
const img = div.querySelector('img');
img && img.parentNode.removeChild(img);

这样的文档仍然有一个副作用,即 DOM 节点不会计算出任何 styles:

$('<img style="cursor: zoom-in">', $sandboxDoc).css('cursor'); // => ''

// by pure javascript
const img = sandboxDoc.createElement('img');
img.setAttribute('style', 'cursor:zoom-in');
img.style.cursor; // => ''

这意味着我们在某些情况下会出现一些意想不到的结果,例如我们无法移除 cursor:

$('<img style="cursor: zoom-in">', $sandboxDoc)
    .css({cursor : ''}).prop('outerHTML'); // => '<img style="cursor: zoom-in">'
    
// by pure javascript
img.style.cursor = '';
img.outerHTML; // => '<img style="cursor:zoom-in">'

为了解决这个问题,我们应该确保在删除文档之前构建 DOM 节点:

function $sandbox(content) {
    const $frame = $('<iframe>').appendTo('body');
    const $elem = $(content, $frame.contents());
    $frame.remove();
    return $elem;
}

$sandbox('<img style="cursor: zoom-in">').css('cursor'); // => 'zoom-in'

// by pure javascript
function sandbox(fn) {
    const frame = document.createElement('iframe');
    document.body.appendChild(frame);
    const elem = fn(frame.contentDocument);
    frame.parentNode.removeChild(frame);
    return elem;
}

sandbox(sandboxDoc => {
    const img = sandboxDoc.createElement('img');
    img.setAttribute('style', 'cursor:zoom-in');
    return img;
}).style.cursor; // => 'zoom-in'

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM