来自系统外部的任何数据都跨越了“信任边界”,需要在系统内部进行验证。这意味着需要服务器端输入验证。
执行输入验证意味着检查您的输入以确保您可以安全地处理它。棘手的部分是,通过验证它,您已经在进行一些次要处理,这可能会产生隐藏的漏洞。所以验证输入有一个特定的顺序。
第一步是验证输入的长度。大多数输入将暂时落在有限的缓冲区中。确保复制到缓冲区的输入量不超过缓冲区的大小——如果输入过多而缓冲区不足,则会产生经典的“缓冲区溢出”问题;利用这些是黑客的主要手段。
下一步是确保数据采用您期望的格式。如果您需要一个数字,请确保字节仅包含数字和您允许的数字符号,例如加号、减号、分隔符、小数点、货币符号等。请注意,这些是特定于语言环境的:在美国,a百万美元可以输入为$1,000,000.00,而在德国,一百万欧元可以输入为1.000.000,00€。如果您需要字母数字字符和数字,请使用“已批准列表”来仅接受您期望的字符。
依赖好角色的批准列表比坏角色的拒绝列表更安全,因为攻击者将来会学习新的攻击。有可能一个意想不到的角色明天会允许我们今天不知道的注入攻击。
请注意,如果您在检查输入长度之前反转这些检查并测试特殊字符,您的验证代码可能容易受到缓冲区溢出的影响。这就是为什么以正确的顺序执行它们很重要的原因。
应该使用输入检查来防止注入攻击似乎很直观(SQL 注入是攻击者输入了一些不好的东西,例如' OR 1=1;DROP TABLE STUDENTS--),但这并不总是可行的。有人可能会试图通过将撇号放入拒绝列表来阻止这种注入,但撇号通常是有效数据,例如 O'Brian 的名字。此外,攻击者通常可以使用 URL 编码等其他策略绕过已批准列表。因此,我们在与 SQL 接口的代码中添加了另一道防线。该代码需要负责尽可能安全地执行查询。这可能是使用参数化 SQL 查询、ORM 或其他防御策略。这样,如果攻击者想出绕过批准列表的方法,参数化 SQL 仍应阻止他们。
注入攻击也不限于 SQL。攻击者会尝试将路径分隔符注入文件名、shell 分隔符(如管道(|))、XML 分隔符、URL 等;你接受的任何东西都可能受到滥用。任何解释用户输入的代码都需要编写以避免此类问题。
验证后的步骤是对输入进行编码以保护输出。例如,如果您要接受<并>稍后在网页上输出结果,您需要确保您对符号进行 HTML 编码,以免无意中造成攻击者可以植入的漏洞<script>attack!</script>在输出页面上。