如何安全地使用 Markdown?

信息安全 应用安全 Web应用程序 xss
2021-09-07 15:21:58

如何安全地使用 Markdown 库?我需要做些什么来确保其输出可以安全地包含在我的网页中?

我想允许不受信任的用户输入内容(以 Markdown 格式)。我将使用 Markdown 处理器生成 HTML,并且我想在我的网页中包含该 HTML。我需要做些什么来确保这是安全的,而不是自己造成的 XSS 漏洞?我需要通过什么论据?我需要做任何预处理或后处理吗?如果相关,我正在使用 python-markdown 库。

2个回答

推荐使用。简短的回答是:使用markdown(untrusted, safe_mode=remove, enable_attributes=False).

确保您拥有最新版本的 Markdown 库,因为旧版本存在一些安全问题。

您还可以通过 HTML 净化器(如 HTML Purifier)运行输出。

基本原理。 禁用enable_attributes. 如果你设置了 Python markdown 库的最新开发版本,默认情况下会禁用enable_attributessafe_mode,但早期版本并没有这样做。因此,在大多数版本的 Markdown 库中,仅设置safe_mode不够的如果你只是设置safe_mode,结果是不安全的:

import markdown
>>> markdown.markdown("{@onclick=alert('hi')}some paragraph", safe_mode=True)
u'<p onclick="alert(\'hi\')">some paragraph</p>'

目前,修复程序仅存在于 git 中。在撰写本文时,最新发布的 Python Markdown 版本(2.1.1)仍然存在漏洞,除非您明确设置enable_attributes=False. 因此,目前使用 Python Markdown 的许多系统可能存在漏洞,这似乎是合理的。

该文档可以更好地警告 Markdown 用户这些陷阱。它说诸如“您可能还想enable_attributes=False在使用时设置safe_mode”之类的内容,但没有透露不这样做会创建一个 XSS 漏洞,其中除了最新版本的库之外的所有版本。更高版本的文档说设置enable_attributes“可能允许不受信任的用户将 JavaScript 注入您的文档”;更清楚地说,该设置enable_attributes确实使用户能够将 Javascript 注入您的文档,因此如果 Markdown 可能来自不受信任的来源,则非常不安全。

怀疑。 也就是说,即使按照上面的建议使用它,我也不能 100% 确定结果是否安全。开发人员发表了如下评论:

“安全模式”是一个糟糕的名称选择,我们继续使用它来进行向后比较(旧代码仍然适用于我们的新版本)。它实际上是一种无标记模式。换句话说,这只是一种禁止原始 html 的方式,并不能保证安全。

这样的评论有点吓人。

在 Python Markdown 库的早期版本中,它的 HTML 清理对我来说看起来有点脆弱,所以我不确定我是否会信任早期版本的 Markdown 库,无论传递了什么标志。考虑以下:

>>> markdown.markdown("[Example](javascript://alert%28%22xss%22%29)", safe_mode=True)
u'<p><a href="javascript://alert%28%22xss%22%29">Example</a></p>'

javascript:在我看来,通过 Markdown 的处理允许-style URL 是一个非常可疑的设计决定。感觉就像是在 XSS 的一跳、一跳和一跳之内。所缺少的只是一种打破 C++ 风格注释 (the //) 的方法,游戏结束了。例如:

>>> markdown.markdown("[Example](javascript://\nalert%28%22xss%22%29)", safe_mode=True)
u'<p><a href="javascript://&#10;alert%28%22xss%22%29">Example</a></p>'

我应该对没有浏览器执行该 Javascript 有多大信心?我不知道,但这并没有给我温暖、模糊的感觉。如果它是安全的,那只是盲目的运气。

幸运的是,如果您设置enable_attributes=False. 但是请确保您设置enable_attributes=False了 ,否则 Markdown 会退回到早期版本中发现的脆弱的 HTML 清理,我对该方案的安全性没有信心。

什么不该做。以下内容不安全:markdown(escape(untrusted)).

  • 您可能认为首先转义输入会删除所有 HTML 并使这种用法安全。事实上,我已经看到它在某些系统中使用并被某些人推荐。但是,它实际上是不安全的,因为转义不足以使 URL 安全。例如,Markdown 的这种用法可以被“ [clickme](javascript:alert%28%22xss%22%29)”打败。一般来说,将输入转义到 Markdown不是正确的方法正确的方法是以适当的方式调用 Markdown(如果您想要额外的保护,也可能将 HTML 过滤器应用于其输出)。

如果你使用 Django。如果您使用 Django,以下应该是使用 Markdown 的安全方法:

{{ untrusted | markdown:"safe" }}

Django 1.4开始,这是安全的。当你传递"safe"参数时,Django 现在特别支持 setsafe_mode和 disable enable_attributes但请确保更新到 Django 1.4 或更高版本;在早期版本中,这种用法是不安全的。

仅 Markdown 不足以净化输出,因为它允许任意 HTML/Javascript 输入并且只是将其传递给未处理。

例如,这是一个有效的降价:

## heading

text

但也是这样:

## heading

text <script>alert('hello');</script>

降价语法页面

对于 Markdown 语法未涵盖的任何标记,您只需使用 HTML 本身。无需为它加上前缀或定界以表明您正在从 Markdown 切换到 HTML;您只需使用标签。

我刚刚使用 python-markdown 做了一个快速测试,它似乎确实以这种方式工作。

也就是说,鉴于 markdown 语法使用的字符集有限,在您将其提供给 markdown之前过滤您允许用户提供的字符集可能会更容易(例如,类似的东西a-zA-Z* #+:/&?=-_()>),但即使这些可能足以混淆一些代码解析/编码它......所以我不确定你纯粹从你使用降价的事实中获得多少安全性。

更新:

经过进一步研究,我在 SO 上找到了这个答案,这似乎很明智。

然后我还进一步搜索并发现了safe_mode开关(在此处此处提到)。

快速测试似乎效果很好,但可能值得进一步研究......

>>> import markdown
>>> markdown.markdown("<script>alert('hello');</script> hello <strong>world</strong>")
u"<script>alert('hello');</script>\n\n<p>hello <strong>world</strong></p>"
>>> markdown.markdown("<script>alert('hello');</script> hello <strong>world</strong>", safe_mode=True)
u'<p>[HTML_REMOVED]</p>\n<p>hello [HTML_REMOVED]world[HTML_REMOVED]</p>'

文档页面上提供了 safe_mode 的完整选项集- 其中还提到enable_attributesFalse为安全起见。