将WMD编辑器的预览HTML与服务器端HTML验证对齐(例如,没有嵌入式JavaScript代码)

时间:2010-05-14 20:59:08

标签: javascript xss wmd wmd-editor

有很多Stack Overflow问题(例如 Whitelisting, preventing XSS with WMD control in C# WMD Markdown and server-side )有关如何对服务器端擦除Markdown产生的问题WMD编辑器确保生成的HTML不包含恶意脚本,如下所示:

<img onload="alert('haha');" 
   src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" />

但我也找不到在客户端堵塞漏洞的好方法。当然,客户端验证不能替代服务器上的擦除验证,因为任何人都可以伪装成客户端并发布令人讨厌的Markdown。如果你在服务器上清理HTML,攻击者就无法保存坏的HTML,因此以后没有其他人能够看到它并且他们的cookie被盗或被坏脚本劫持的会话。因此,有一个有效的案例可能不值得在WMD预览窗格中强制执行无脚本规则。

但是想象一下,攻击者发现了一种将恶意Markdown带入服务器的方法(例如来自其他站点的受感染的源,或者在修复XSS错误之前添加的内容)。将markdown转换为HTML时应用的服务器端白名单通常可以防止向用户显示错误的Markdown。但是,如果攻击者可以让某人编辑该页面(例如,通过发布另一个条目,说恶意条目有一个断开的链接并要求某人修复它),那么编辑该页面的任何人都会被劫持。这无疑是一个极端情况,但它仍然值得防范。

此外,允许客户端预览窗口允许与服务器允许的HTML不同的HTML可能是个坏主意。

Stack Overflow团队通过更改WMD来堵塞这个漏洞。他们是怎么做到的?

[注意:我已经想到了这一点,但它需要一些棘手的JavaScript调试,所以我在这里回答我自己的问题,以帮助其他可能想要做同样事情的人] 。< / p>

2 个答案:

答案 0 :(得分:6)

一种可能的解决方法是在pushPreviewHtml()方法中的wmd.js中。以下是Stack Overflow version of WMD on GitHub的原始代码:

if (wmd.panels.preview) {
    wmd.panels.preview.innerHTML = text; 
}

您可以使用一些清理代码替换它。以下是Stack Overflow使用in response to this post的代码的修改,它限制了标签的白名单,对于IMG和A元素,限制为属性的白名单(并且也按特定的顺序!)。有关白名单的更多信息,请参阅Meta Stack Overflow帖子 What HTML tags are allowed on Stack Overflow, Server Fault, and Super User?

注意:此代码当然可以改进,例如以任何顺序允许列入白名单的属性。它也不允许mailto:URL在Internet网站上可能是一件好事,但在您自己的Intranet网站上它可能不是最好的方法。

if (wmd.panels.preview) {

    // Original WMD code allowed JavaScript injection, like this:
    //    <img src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" onload="alert('haha');"/>
    // Now, we first ensure elements (and attributes of IMG and A elements) are in a whitelist,
    // and if not in whitelist, replace with blanks in preview to prevent XSS attacks 
    // when editing malicious Markdown.
    var okTags = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;
    var okLinks = /^(<a\shref="(\#\d+|(https?|ftp):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+)"(\stitle="[^"<>]+")?\s?>|<\/a>)$/i;
    var okImg = /^(<img\ssrc="https?:(\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+)"(\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\salt="[^"<>]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i;
    text = text.replace(/<[^<>]*>?/gi, function (tag) {
        return (tag.match(okTags) || tag.match(okLinks) || tag.match(okImg)) ? tag : ""
    })

    wmd.panels.preview.innerHTML = text;  // Original code 
}

另请注意,此修复程序不在Stack Overflow version of WMD on GitHub中 - 显然更改是在稍后进行的,而不是检入GitHub。

更新:为了避免破坏在您输入网址时自动创建超链接的功能,您还需要对showdown.js进行更改,如下所示:

原始代码:

var _DoAutoLinks = function(text) {

    text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");

    // Email addresses: <address@domain.foo>

    /*
        text = text.replace(/
            <
            (?:mailto:)?
            (
                [-.\w]+
                \@
                [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
            )
            >
        /gi, _DoAutoLinks_callback());
    */
    text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
        function(wholeMatch,m1) {
            return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
        }
    );

    return text;
}

固定代码:

var _DoAutoLinks = function(text) {
    // use simplified format for links, to enable whitelisting link attributes
    text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4");
    text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, '<a href="$1">$1</a>');
    return text;
}

答案 1 :(得分:2)

允许本地用户在页面上下文中执行脚本不是安全问题,只要任何第三方都无法提供脚本。 如果没有编辑器,用户可以随时在您的页面上输入javascript: URL或使用Firebug或类似的东西。