3

markdownの代わりに、JavaScript で構成可能な HTML のサブセットを安全に解析する方法を探しています。

たとえば、(信頼できない)入力の場合

<b onclick="alert('XSS');" data-myapp="asdf" style="color:red">Hello</b>
<h1><i style="color:expression(alert('XSS'));"> World</i></h1>

パラメータ付き

allowTags: b, i
allowAttrs: data-myapp
allowSafeStyle: color

私は出力を期待します

<b data-myapp="asdf" style="color:red">Hello</b>
<i> World</i>

Markdown は、より複雑なプロパティを表現できないようです。Cajaは私が望むものにかなり近いようですが、サーバー側のレンダリングが必要です。では、安全なallowTags(上記のパラメーター、などに従ってallowAttrs) HTML のサブセットを JavaScript でレンダリングするにはどうすればよいでしょうか?

4

1 に答える 1

1

jQuery を使用して回答を短くし、ボイラープレート コードを少なくしますが、関連性はありません。

.innerHTMLhtmlで可能なスクリプトやcssを実行しないので使っています。

デモはこちらhttp://jsfiddle.net/QCaGq/

function filterData(data, options ){
    var root;

    try {
        root = document.implementation.createHTMLDocument().body;
    }
    catch(e) {
        root = document.createElement("body");
    }

    root.innerHTML = data;

    $(root).find("*").filter(function(){
        return options.allowTags.indexOf(this.tagName.toLowerCase()) === -1;
    }).each( function() {
        $(this).children().detach().insertBefore( this );
        $(this).remove();
    });

    function removeStyle( node, attr ) {
        var style = node.style,
            prop,
            name,
            len = style.length,
            i, val = "";

        for( i = 0; i < len; ++i ) {
            name = style[i];
            prop = style[name];

            if( options.allowSafeStyle.indexOf( name ) > -1 ) {
                val += (name + ":" + prop + ";");
            }
        }

        if( val ) {
            attr.nodeValue = val;
        }
        else {
            node.removeAttribute("style");
        }
    }

    function removeAttrs( node ) {
        $.each( node.attributes, function( index, attr ) {

            if( !attr ) {
                return;
            }

            if( attr.name.toLowerCase() === "style" ) {
                return removeStyle( node, attr );
            }

            if( options.allowAttrs.indexOf(attr.name.toLowerCase()) === -1 ) {
                node.removeAttribute(attr.name);
            }
        });
    }

    function walk( root ) {
        removeAttrs(root);
        $( root.childNodes ).each( function() {
            if( this.nodeType === 8 ) { //Remove html comments
                $(this).remove();
            }
            else if( this.nodeType === 1 ) {
                walk(this);
            }
        });
    }

    walk(root);

    return root.innerHTML; 
}

var opts = {
    allowTags: ["b", "i"],
    allowAttrs: ["data-myapp"],
    allowSafeStyle: ["color"]
}

filterData( '<b onclick="alert(\'XSS\');" data-myapp="asdf" style="color:red">Hello</b>\n<h1><i style="color:expression(alert(\'XSS\'));"> World</i></h1>', opts );

結果:

<b data-myapp="asdf" style="color:red;">Hello</b>
<i> World</i>

これで始められるはずです。

于 2012-07-03T12:04:02.213 に答える