在Django模板中安全地包含JavaScript数据

Django模板通常用于将数据传递到JavaScript代码。 不幸的是,如果实施不正确,这将打开HTML注入的可能性,从而引发XSS(跨站点脚本)攻击。

这是我在Django项目中遇到的最常见的安全问题之一。 实际上,我可能已经在每个相当大的Django项目中以某种形式看到了它。

很难正确! 从历史版本上看也很困难,因为只有Django 2.1才添加了json_script模板标签来安全地执行此操作。

让我们看一下问题以及如何使用json_script修复它。

危险的方法:
让我们看一下这个view:

模板:

不幸的是,该模板可以进行HTML注入。 这是因为如果数据在任何地方都包含</ script>,则结果的其余部分将被解析为额外的HTML。 我们称这种HTML注入,攻击者可以使用它向您的网站添加任意(邪恶的)内容。

如果mydata可由第三方以任何方式控制,例如用户的注释或API的返回数据,则攻击者可能会尝试将其用于HTML注入。

想象一下get_mydata()返回了这个狡猾的字符串:

(我使用的是字符串,但这也适用于字典和列表,因为在JavaScript中它们也可以包含字符串。)

然后,该模板将呈现为:

浏览器首先仅通过HTML标签解析页面-无需检查其中的JavaScript。

因此,它会看到第一个<script>在mydata =“之后关闭。它将尝试运行该JavaScript,它将因不完整的字符串错误而崩溃。

然后它将解析第二个注入的<script>标记作为页面的合法部分。 这意味着它将加载evil.js。

最后,它将尾随的“;”呈现为文本,并忽略最后一个</ script>,因为它与开头的<script>不匹配。

evil.js可能会做一些恶作剧,例如窃取用户的会话Cookie并将其发送给攻击者。

当心“safe”
如果不使用| safe,我们的模板将是安全的。 每当我们使用安全模板过滤器时,我们真正要说的是“我保证此数据可以直接包含在HTML中是安全的”。 事实并非如此。

如果我们将其从模板中删除:

那么我们就不会对上述攻击持开放态度。 但是数据不会按预期呈现:

由于所有HTML实体均已转义,因此该字符串将无法按预期在JavaScript中使用。 否则,您需要编写额外的JavaScript来使其解脱,这也为再次攻击提供了机会。

另一种脆弱的方式
另一个常见的易受攻击的模式是在视图中使用json.dumps(),然后在模板中将该值称为“安全”。 例如,采取以下view:

模板:

这看起来更安全,因为我们将数据序列化为JSON,并使用“安全”模板过滤器。 不幸的是,它同样脆弱,因为它也不安全。

再次想象一下,mydata还是与上面相同的字符串。 那将使mydata_json等于:

(在json.dumps中加双引号,将其转换为存储在Python字符串中的JSON字符串。)

然后,该模板将呈现为:

同样,我们有同样的问题。 浏览器会将HTML解析为不完整的<script>,然后解析为另一个<script>以包含evil.js,然后包含文本“;”,最后是被忽略的</ script>

安全方式
使用Django避免此漏洞的最佳方法是使用json_script模板标签。 这通过使用JSON脚本标签以HTML注入证明的方式输出数据。

在我们的模板中,我们将像这样使用它:

将被渲染为:

这是一个<script>,但是由于其类型是“ application / json”而不是JavaScript类型,因此浏览器不会执行它。 Django已将所有HTML敏感字符替换为其JSON字符串Unicode转义形式,例如\ u003C。 因此,浏览器将永远不会看到任何结束</ script>标记或类似标记。

我们还需要更改JavaScript以从该元素中获取数据。 改编自Django文档,最终结果如下所示:

与CSP同行

如果您想更加安全,可以再走一步,避免在模板中完全使用内联<script>标记。 也就是说,将您的JavaScript移到其自己的静态文件mypage.js中:

模板:

您可以通过禁止站点上的内联脚本来进一步降低XSS风险。 您可以使用script-src指令通过内容安全策略(CSP)来实现。 有关更多信息,请参阅我的文章“如何在Django网站上为安全标头评分A +”。

那关于“ escapejs”呢?

Django还提供了escapejs模板标记,看起来它可以工作。 您可能会想这样使用它:

如文档所述,不幸的是并不安全:

转义用于JavaScript字符串的字符。 这不能确保在HTML或JavaScript模板文字中使用该字符串的安全性,但是可以在使用模板生成JavaScript / JSON时保护您免受语法错误的影响。

通常它也没有用,因为它仅适用于字符串,不适用于字典或列表。

最后
我希望这可以帮助您编写更安全的Django应用程序。

原文:https://adamj.eu/tech/2020/02/18/safely-including-data-for-javascript-in-a-django-template/