jQuery安全注入html



所以我通过json从外国不受信任的来源接收html。我想在div容器中显示html,如下所示:

$('#container').html(dangerousHTMLCode);

我怎样才能防止最重要的javascript注入,其次是改变页面的样式。这都是客户端。容器div应该是一个灵活的高度,以匹配内容的高度(可能排除一些iframe解决方案)。

Update:目标是从html中去掉所有的javascript和css。这包括存在于dom项属性中的js和css (style=", onclick="等)

好吧,我的第一次尝试失败了。我同意Jan Dvorak的评论,最好的方法可能是在服务器端代理中使用XSS工具,特别是因为你可能不得不通过某种代理,因为你正在做跨站点请求,如果你使用JSONP,一切都已经丢失了。

然而,既然这个问题要求用jQuery的方式来做这件事…

理想情况下,您应该找到一个用javascript编写的HTML解析器,使用它来构建一个元素树,并删除任何与安全属性白名单不匹配的元素或属性。

由于我不知道这样的解析器,并且由于您在浏览器中确实有解析器,我们将尝试使用它。不过,我们必须小心,该解析器附加到javascript引擎和HTTP客户端,以及其他东西。

首先,正如在我第一次尝试的反馈中指出的那样,在我们做任何将创建DOM元素的事情之前,我们必须做一些工作,因为一些事件可以在DOM插入之前运行。我们至少需要确保在创建任何DOM对象之前不会解析任何onX属性。通常在预加载中运行一些干扰也是一个好主意。为此,让我们做一些简单的文本转换:

var xmlNameStartChars = "a-zA-Z_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff\0370-\u037d\u037f-\u1fff\u200c\u200d\u2070-\u2218f\u2c00-\u2fef\u3001-\udbbf\udc00-\udfff\uf900-\ufdcf\ufdf0-\ufffd";
var xmlNsPfx = "[" + xmlNameStartChars + "][-.0-9\u00b7\u0300-\u036f\u203f-\u2040" + xmlNameStartChars + "]*:";
var tagStartRE = new RegExp("<\/?(" + xmlNsPfx + ")?", "g");
var tagStartDeZRE = new RegExp("(<\/(" + xmlNsPfx + ")?)z", "g");
dangerousHTMLCode = dangerousHTMLCode.replace(/on/gi, "z$&"); // run interference with onX
// run interference with preloading
// But don't interfere with namespaces
dangerousHTMLCode = dangerousHTMLCode.replace(/</?(w*:)?/g, "$&z");

现在,我们已经尽了最大的努力使其安全构建DOM树。但是,请注意,这是不是的安全保证-很可能存在我没有考虑到的攻击,可能围绕过去,现在或未来浏览器或插件的错误。一个特别值得关注的问题是,我已经做出了这样的假设:在这一点上,唯一危险到需要干预的属性是从"on"开始的;我认为是这样,但我远没有100%的信心。

继续,风险自负

正如Jan在我第一次尝试的评论中向我指出的那样,白名单方法可能优于黑名单方法。我将从一个非常基本的白名单元素列表开始,根据口味添加/删除;我们将用z s作为它们的前缀,因为我们的文本操作也是这样做的。

var wlElements = "zdiv, zspan, zem, zstrong, zp, za, zimg, ztable, zthead, ztbody, ztfoot, ztr, zth, ztd";
var nonWlSelector = ":not(" + wlElements + ")";
var dangerousDOM = $("<div/>").html(dangerousHTMLCode);
dangerousDOM.find(nonWlSelector).remove();

现在进入有趣的部分,您必须删除危险属性。这次我把它们列入了黑名单,部分原因是我懒得去想我想列入白名单的所有属性……但是我确实在src和href中加入了URL方案白名单,不仅仅是"javascript:"有潜在的不安全,"vbscript:"one_answers"livescript:"在某些浏览器中至少也是危险的。您可能应该将属性列入白名单,例如,我很有可能忘记或从不知道脚本属性不以"on"开头。我还没有找到一种不进行暴力DOM遍历就能找到"坏"属性的方法,所以让我们这样做:

var badAttrs = /^(.*:)(zon|style|background)/i;
var suspectAttrs = /^(.*:)(src|href)$/i;
var goodSchemes = /^s*([^:]*$|ftp:|tel:|https?:)/i;
function processAttributes(element) {
    var toRemove = [];
    var attrs = element.attributes;
    for (var i = 0; i < attrs.length; i++) {
        var name = attrs[i].name, val = attrs[i].value;
        if (badAttrs.test(name) || (suspectAttr.test(name) && !goodSchemes.test(val)) {
            toRemove.push(attrs[i].name);
        }
    }
    while (toRemove.length) {
        element.removeAttribute(toRemove.pop());
    }
}
// Start walking from the root of our DOM fragment
var root = dangerousDOM[0];
var elements = [root];
// Walk until we have no more elements, processing their attributes and adding their children
while (elements.length) {
    var elem = elements.pop();
    if (elem.hasAttributes()) {
        processAttributes(elem);
    }
    // Find children of this element and queue them up
    child = elem.firstChild;
    while (child) {
        if (child.nodeType == 1) {
            // It's an element
            elements.push(child);
        }
        child = child.nextSibling;
    }
}

现在我们准备撤销我们在开始时所做的文本操作并注入片段。同样,我们尽最大努力使其更安全,可能没有考虑到今天的攻击,并且仍然可能允许攻击未来的浏览器/插件错误。所以,还是那句话,继续下去,风险自负。

var lessDangerousHtml = dangerousDOM.html();
lessDangerousHtml = lessDangerousHtml.replace(/z(on)/gi, "$1");
lessDangerousHtml = lessDangerousHtml.replace(tagStartDeZRE, "$1");
$("#container").html(lessDangerousHtml);

您可以使用iframe并将不受信任的HTML加载到该iframe中。你所拥有的iframe将使用sandbox属性来防止在iframe内部注入任何JavaScript来修改iframe外部的环境。

<iframe class="untrusted" src="http://unsafe.example.com/" sandbox />

或者如果不受信任的HTML是JSON,那么在服务器上删除JSON。

<iframe class="untrusted" src="/Unsafe?url=http://unsafe.example.com/foo.json" sandbox />

最新更新