好问题!你在问正确的问题。
简短的回答。 在大多数情况下,在输出端转义是最重要的事情。最好的解决方案是使用 Web 开发框架(例如 Google ctemplate),该框架提供上下文相关的自动转义和针对其他注入攻击的自动防御(例如用于避免 SQL 注入的准备语句)。这可能比输入端的消毒更有效。
解释。在这里,我们有一个不受信任的数据流,来自某个输入源(例如,URL 参数),通过复杂的计算链(例如,通过数据库),最后到某个输出接收器(例如,HTML 模板中的动态内容)。我们应该把消毒/逃逸放在哪里?我们可以把它放在输入附近,或者靠近输出,或者在中间的某个地方。我们如何决定把它放在哪里最好?我想这就是你要问的。
难题的第一部分是认识到最好有一个一致的政策。最好把所有东西都放在输入端,或者把所有东西放在输出端,而不是清理 50% 的输入和 50% 的输出(如果你这样做,那么很难检查你的政策是否得到遵守始终如一的,并且很容易导致从不受信任的源到输出接收器的数据流永远不会被清理/转义)。最好有一个策略,“数据库中的所有内容都已被清理和转义,并且都可以被视为受信任”或“数据库中没有任何内容被清理/转义,并且都应该被视为不受信任”(或制定一个政策,记录数据库中的哪些字段可以被信任已经被清理/转义,
谜题的第二部分是问:我需要知道哪些额外的信息才能正确地进行清理/转义?我是否需要知道一些关于不受信任的输入来自何处的信息?我是否需要知道一些关于它将在哪里使用的信息(它将被插入到输出的哪一部分)?
在大多数情况下,答案是:我们需要知道不受信任的数据将在哪里使用(它将出现在 HTML 输出中的什么位置),而不是它的来源。我们需要知道它会被插入到 HTML 文档的哪个位置,因为这决定了转义函数的选择:如果它被插入到标签之间,那么你应该使用 HTML 转义来转义<
, >
, 和&
; 如果它被插入到一个属性中,那么你也需要转义引号;如果它是作为 URL 插入的,那么您还需要检查协议方案(以确保它不是javascript:
网址)。此信息在输出接收器处很容易获得,但在输入源处不可用。如果您在输出端执行转义,则此信息很容易获得:当您将动态数据插入 HTML 文档时,您将获得所有需要的关于它将插入的解析上下文的信息,触手可及。另一方面,如果您尝试对输入源进行清理,则不清楚数据可能会在哪里使用,因此很难知道需要如何对其进行转义。因此,这建议在输出接收器处转义,而不是在输入源处进行消毒。
第三个难题是存在执行上下文相关自动转义的 Web 编程框架。通常,他们使用模板系统,对于将动态插入到模板中的每个值,他们查看将插入的 HTML 上下文(是在标签之间吗?在属性中?URL 值?在 Javascript 中?) ,弄清楚需要使用什么转义函数,然后自动应用那个转义函数。这是一个巨大的胜利,因为它确保使用了正确的转义函数,并消除了您忘记转义某些值的漏洞。今天,这两种漏洞都很常见:开发人员经常忘记转义某些值,而当他们确实记得转义时,他们经常为将要使用该值的上下文应用错误的转义函数。上下文相关的自动转义应该从根本上消除这些漏洞。
讨论。也就是说,最好的防御是在输出处使用上下文相关的转义,在输入处使用输入验证/清理。我认为上下文敏感逃脱了你最重要的防线。但是,在输入处清理值(基于您对有效数据应该是什么样子的期望)也是一个好主意,作为一种纵深防御的形式。它可以消除或减轻某些类型的编程错误,从而使利用它们变得更加困难或不可能。