将 WMD 编辑器的预览 HTML 与服务器端 HTML 验证对齐(例如,没有嵌入的 JavaScript 代码)
Posted
技术标签:
【中文标题】将 WMD 编辑器的预览 HTML 与服务器端 HTML 验证对齐(例如,没有嵌入的 JavaScript 代码)【英文标题】:Align the WMD editor's preview HTML with server-side HTML validation (e.g. no embedded JavaScript code) 【发布时间】:2011-02-19 17:22:12 【问题描述】:关于如何对 WMD 编辑器生成的 Markdown 进行服务器端清理以确保生成的 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。但是,如果攻击者可以让某人编辑该页面(例如,通过发布另一个条目说该恶意条目的链接已损坏并要求某人修复它),那么任何编辑该页面的人都会被劫持他们的 cookie。诚然,这是一个极端案例,但它仍然值得防御。
另外,允许客户端预览窗口允许与服务器允许的不同的 HTML 可能是个坏主意。
Stack Overflow 团队通过对 WMD 进行更改来填补这个漏洞。他们是怎么做到的?
[注意:我已经弄清楚了,但它需要一些棘手的 javascript 调试,所以我在这里回答我自己的问题,以帮助其他可能想做同样事情的人]。
【问题讨论】:
【参考方案1】:一个可能的修复方法是在 wmd.js 中的 pushPreviewHtml()
方法中。这是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\s-s-rc="https?:(\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+)"(\s)?(\s)?(\s<>]*")?(\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。
更新:为了避免破坏在您输入 URL 时自动创建超链接的功能,您还需要对 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;
【讨论】:
我不相信这是需要解决的问题。它看起来像是寻找问题的解决方案。或许你在 *** 版本的 WMD 中没有看到这段代码的原因是因为它不存在,因为它不需要。 是的,我也不相信它是必要的。也就是说,***.com 的人实现了这一点,以确保预览器永远不会生成他们的服务器端验证器不接受的 HTML。似乎是合理的,虽然我同意不是一个非常高的优先级。请参阅meta.stackexchange.com/questions/1227/… 了解有关 SO 为何这样做的更多详细信息。顺便说一句,我刚刚编辑了我的问题以符合 SO 想要这样做的实际原因。 您可以通过 AJAX 发布 HTML 并从服务器取回经过清理的 HTML,以获得完美的预览。 @Justin Grant:您是否在任何地方都有应用了更改的开源存储库?我正在考虑在 Github 上分叉 WMD。 (感谢您发布此问题/答案!) 这可能是一个不错的 HTML sanitizer:code.google.com/p/google-caja/wiki/JsHtmlSanitizer.【参考方案2】:只要任何第三方都不可能提供脚本,允许本地用户在页面上下文中执行脚本就不是安全问题。
如果没有编辑器,用户总是可以在您的页面上输入javascript:
url,或者使用 Firebug 或类似的东西。
【讨论】:
一开始我同意你的看法,但我确实发现了一个有趣的案例:如果攻击者有其他方法可以在服务器上获取受损的 Markdown,那么这个 WMD 预览问题将是危险的。如果攻击者可以让网站版主编辑错误页面(例如清除损坏的链接),那么他就有可能控制整个网站。诚然,在发送到编辑页面之前,通过在服务器上清除 markdown 很容易被击败,但输出验证通常被忽略。诚然,这是一个极端案例,但可能值得为安全站点进行防御。以上是关于将 WMD 编辑器的预览 HTML 与服务器端 HTML 验证对齐(例如,没有嵌入的 JavaScript 代码)的主要内容,如果未能解决你的问题,请参考以下文章
WMD markdown 编辑器 - HTML 到 Markdown 的转换
将 HTML 转换回 Markdown 以便在 wmd 中进行编辑