在数据库之前或显示时过滤用户输入?

信息安全 Web应用程序 数据库 xss 验证
2021-09-02 04:28:30

给定一个必须正确转义用户数据以避免 XSS 的 Web 应用程序,是在“坏东西”进入数据库之前尝试删除它更好,还是最好允许它在数据库中但要小心转义输出时它显示在页面上?

我看到一些应用程序的输入是原始存储在数据库中,但输出总是被转义(至少到目前为止——我还在寻找!)。在数据库中看到恶意数据让我感到不舒服,因为输出的安全性依赖于开发人员在每次输出时记住转义字符串......(某种框架会更好,至少它会收集输出代码和过滤/转义到一个公共位置。)

编辑

为了清楚起见:我正在审核现有的 Web 应用程序,而不是开发. (至少出于这个问题的目的——当我做 web 开发时,我会找到一个框架。)我看到的很多东西都使用了 ad hoc 过滤和/或转义输入和/或输出。@DW 的回答一针见血——触及了我所问的本质。

4个回答

好问题!你在问正确的问题。

简短的回答。 在大多数情况下,在输出端转义是最重要的事情。最好的解决方案是使用 Web 开发框架(例如 Google ctemplate),该框架提供上下文相关的自动转义和针对其他注入攻击的自动防御(例如用于避免 SQL 注入的准备语句)。这可能比输入端的消毒更有效。

解释。在这里,我们有一个不受信任的数据流,来自某个输入源(例如,URL 参数),通过复杂的计算链(例如,通过数据库),最后到某个输出接收器(例如,HTML 模板中的动态内容)。我们应该把消毒/逃逸放在哪里?我们可以把它放在输入附近,或者靠近输出,或者在中间的某个地方。我们如何决定把它放在哪里最好?我想这就是你要问的。

难题的第一部分是认识到最好有一个一致的政策。最好把所有东西都放在输入端,或者把所有东西放在输出端,而不是清理 50% 的输入和 50% 的输出(如果你这样做,那么很难检查你的政策是否得到遵守始终如一的,并且很容易导致从不受信任的源到输出接收器的数据流永远不会被清理/转义)。最好有一个策略,“数据库中的所有内容都已被清理和转义,并且都可以被视为受信任”或“数据库中没有任何内容被清理/转义,并且都应该被视为不受信任”(或制定一个政策,记录数据库中的哪些字段可以被信任已经被清理/转义,

谜题的第二部分是问:我需要知道哪些额外的信息才能正确地进行清理/转义?我是否需要知道一些关于不受信任的输入来自何处的信息?我是否需要知道一些关于它将在哪里使用的信息(它将被插入到输出的哪一部分)?

在大多数情况下,答案是:我们需要知道不受信任的数据将在哪里使用(它将出现在 HTML 输出中的什么位置),而不是它的来源。我们需要知道它会被插入到 HTML 文档的哪个位置,因为这决定了转义函数的选择:如果它被插入到标签之间,那么你应该使用 HTML 转义来转义<, >, 和&; 如果它被插入到一个属性中,那么你也需要转义引号;如果它是作为 URL 插入的,那么您还需要检查协议方案(以确保它不是javascript:网址)。此信息在输出接收器处很容易获得,但在输入源处不可用。如果您在输出端执行转义,则此信息很容易获得:当您将动态数据插入 HTML 文档时,您将获得所有需要的关于它将插入的解析上下文的信息,触手可及。另一方面,如果您尝试对输入源进行清理,则不清楚数据可能会在哪里使用,因此很难知道需要如何对其进行转义。因此,这建议在输出接收器处转义,而不是在输入源处进行消毒。

第三个难题是存在执行上下文相关自动转义的 Web 编程框架。通常,他们使用模板系统,对于将动态插入到模板中的每个值,他们查看将插入的 HTML 上下文(是在标签之间吗?在属性中?URL 值?在 Javascript 中?) ,弄清楚需要使用什么转义函数,然后自动应用那个转义函数。这是一个巨大的胜利,因为它确保使用了正确的转义函数,并消除了您忘记转义某些值的漏洞。今天,这两种漏洞都很常见:开发人员经常忘记转义某些值,而当他们确实记得转义时,他们经常为将要使用该值的上下文应用错误的转义函数上下文相关的自动转义应该​​从根本上消除这些漏洞。

讨论。也就是说,最好的防御是在输出处使用上下文相关的转义,在输入处使用输入验证/清理我认为上下文敏感逃脱了你最重要的防线。但是,在输入处清理值(基于您对有效数据应该是什么样子的期望)也是一个好主意,作为一种纵深防御的形式。它可以消除或减轻某些类型的编程错误,从而使利用它们变得更加困难或不可能。

我强烈建议使用现有框架对输入进行验证,并对输出进行转义。

输入转义有三个大问题:

  • 您必须为不同的输出媒体取消转义和重新转义,例如 pdf 文件而不是 HTML 页面
  • 引入修复更加复杂,因为需要审查现有的(不完全转义的)数据。
  • 使用低级数据库诊断工具查看数据只会显示转义数据。

更重要的是:逃避输入对你的好处很少,因为在输入上忘记它比在输出上容易忘记。两者都做显然是行不通的,因为它会破坏数据。

您应该使用一个框架(例如 JSF),让域开发人员免于必须牢记转义的负担。这将导致显示组件的技术部分的代码量非常少,需要充分了解正确的转义。少量的关键代码是好的,因为它极大地减少了错误的机会并简化了审计和教育。

你总是过滤掉影响你放置它的地方的“坏东西”。

因此,如果您将数据发送到数据库,您就会逃避数据库注入。如果您要发送到 HTML,则您会转义以进行 HTML 注入。这意味着你要逃跑两次,但是是为了不同的事情。

这不仅仅是偏好问题。相反,这可以确保您的应用程序在未来保持安全。例如,如果您在插入数据库时​​对数据进行 HTML 转义,那么如果恶意用户找到将 HTML 导入数据库的方法,那么数据库就会成为脚本和其他基于 HTML 的攻击的潜在载体。

换句话说,通过尽可能接近漏洞利用点,您可以减少整体攻击面。

理想情况下所有三个。假设在某些时候,在输入屏幕上工作的开发人员会遗漏检查(或者更有可能出现一些新的漏洞利用来破坏检查)。输入验证的困难在于,如果出现新的漏洞,您可能在修复验证之前已经接受了恶意数据。假设,在某些时候,开发人员处理输出将忽略过滤(或新的漏洞利用破坏过滤器)。

因此,如果可能的话,在输入、输出和数据库约束中进行验证。每当更改约束时,所有现有数据都将根据新规则重新验证。

如果我必须选择一个,那将是输出,因为增强功能将应用于新的和现有的数据。