接受用户输入或来自不受信任来源的任何其他数据是 PHP 开发人员在开发应用程序时可能承担的最常见风险之一。您经常需要引入来自未知来源的数据以使应用程序运行,但是这就给黑客提供了插入任意代码或以其他方式 使用应用程序的机会。从 PHP V5.2 开始,输入过滤扩展功能将被默认启用,以使您可以更轻松地针对此类操作采取措施。输入过滤扩展功能提供了一组函数来解析和检查输入,然后在函数中使用此输 入。
我们将考察使用这些函数解析和检查输入而不进行手动编码的原因,并介绍一些如何使用这些新函数的基本示例。
输入是大多数应用程序的关键。我们的应用程序将接收大量信息并使用微处理器的能力来处理这些信息。我们可以控制输入进入应用程序的位置,但是我们不 能控制输入此数据的用户的意图。PHP 最初被开发为一种轻松的方法,用于开发从 HTML 表单收集输入的脚本。它现在的功能早已不限于此了,但是我们现在仍然在用它来收集和操作来自许多不受信任的源的数据。即使是单独的数据输入职员也可能无意 识地向应用程序发送一些问题数据,甚至也必须将其视为不受信任的源。
安全是目前十分热门的问题,并且随着每次通过的硬件和软件修订而变得更加热门。我们总是说得很好听,但是由于情况很复杂,因此我们往往在系统保护的 关键点 —— 输入上做得差强人意。方法有很多并且实现起来很复杂。我们完成应用程序有严格的最后期限并且必须履行项目经理的最后期限。
保护自己免于恶意代码攻击的一种方法是确保接收到预期的输入。在本文中,我们将查看一个阻止用户输入不适当的 JavaScript 的示例,并向您展示如何将那些标记从输入中剥离出来,返回实际需要的内容。
PHP V5.0 以后的版本中提供了输入过滤扩展功能以使您可以更轻松地获得安全性。PHP V5.2 版本首次默认启用了此功能并实现了开箱即用。
在您的编程经历中可能听说过术语:输入验证 。它是应用程序开发过程中至关重要的部分,用于确保传入数据在上下文和内容上都是正确 的。程序员可以通过正则表达式和测试来查看值是否满足标准(例如,要求输入电子邮件地址并需要确保输入格式正确时)。如果需要排除 Hotmail 或 Gmail 等免费电子邮件地址,也可以借助此方法。您将使用正则表达式来阻止包含 “hotmail.com” 或 “gmail.com” 的任何电子邮件地址。
通常认为这类 “处理基本检查以确保输入数据满足某种标准” 就是验证。在这种情况下,需要确保不必清理数据,或者确实可获得查找的数据。验证的目标就是要避免一些简单错误,例如尝试将 NULL 放入不接受这种值的字段。
错误的数据或恶意构造的数据导致的错误可能带来毁灭性的后果。您将需要全力实现一种更全面的过滤此信息的方法,这种方法需涵盖针对每种数据类型的所 有可能测试,而不是执行千篇一律的任务或重复的任务。通常,您可能没注意到测试或编写不完整的正则表达式。过滤器扩展功能将帮助提供更完整的输入评估,同 时减少重复代码编写工作。在这种情况下,过滤与验证的不同之处就在于过滤在安全性上更全面。
让我们进一步查看扩展功能的详细信息。过滤扩展功能有两种过滤器:Sanitizing 和 Logical。
Sanitizing 过滤器只是允许或禁止字符串中的字符并将清理后的字符串作为结果返回。无论您将哪种数据格式传入这些函数,它们将始终返回字符串。对于特定类型的使用,这 是至关重要的,因为您可以阻止用户发送不适当的输入并导致异常结果。例如,用户可以发现文本块的输入被返回到以下页面上并且要利用那些返回信息。如果清理 输入,则将删除输入的所有危险部分。
Logical 过滤器将对变量执行测试并根据测试提供 true 或 false 结果。然后您可以使用结果来决定如何处理数据或获得用户的地址。这种过滤器的简单示例是验证年龄。逻辑测试还可以针对类似 Perl 的正则表达式进行测试。
让我们看看在进行过滤和验证时使用这些函数的一些方法,了解如何将过滤引入应用程序。
让我们开始使用过滤扩展功能的清理应用程序来过滤不需要的代码块。
在这个示例应用程序中,您有一张简单的表单,它接收三个问题和三个答案。表单本身没有使用验证或过滤。查看源代码时,一个不幸的用户相信这是真的, 并且决定通过编写包括抛出警报的 JavaScript 调用的文本块来测试表单。如果警报弹出,告诉他输入的所有内容都将真正被接受并由应用程序使用而不进行过滤,这将证明他是正确的。
|
清单 1 是接受用户输入的常见 HTML 表单。要求用户提供一些基本信息并且简单地将其返回,而不进行任何其他操作。在图 1 中,您可以看到用户已经输入了一些信息并准备发送 JavaScript 代码,如果不完成清理工作,这段 JavaScript 代码将发送警报。
接下来,创建一个脚本来捕获表单变量并在另一个页面中将其打印输出。
|
现在您可以看到如果只是返回这个输入而不使用任何类型的过滤将会发生什么情况。
如您所见,不幸的用户已经执行了一些随机代码。假定,这段代码只位于客户端(不幸用户自己的 PC 计算在内,并且在这种情况下不表示对应用程序构成威胁),但是它肯定不是有效输入。
清单 3 演示了使用 filter_var()
的 FILTER_SANITIZE_STRING
选项进行清理的简单示例。
|
如您所见,开始使用 filter_var()
函数来清理输入并使其有效并且安全。在这种情况下,使用选项 FILTER_SANITIZE_STRING
,该选项将获取输入、删除所有 HTML 标记并选择性地编码或删除特定字符。
由于它将除去 HTML 标记,因此尝试运行 JavaScript 将失败,并且从脚本中获得更适当的结果。
与尝试用正则表达式和字符串函数手动完成此工作相比,使用 filter_var()
函数将极大地减少编码量。在那种情况下,您将必须编写只执行 HTML 代码块的另外若干行代码和显式正则表达式,并且解析整个输入的文本块以确保没有遗漏任何 HTML 标记。而使用提供的函数将更简单。
一些输入是数字或有指定的式样。在这些情况下,您可以使用逻辑过滤选项并针对表达式或其他逻辑测试检查数据。如果表达式读到 true,则数据将成功通过过滤器。如果逻辑测试证明数据为 false,则数据将不能通过过滤器。首先,我们将设置一个简单的逻辑测试用于检查确保燕子坠落的速度在规定的范围内,因为燕子的空中速度可能不同。设置 完测试后,我们将添加一些代码来告诉用户答案是否不正确。正确答案应当只需重复。
|
清单 4 将根据速度的最小值和最大值范围检查坠落的燕子问题的答案。这可以简单地确保它确实是一个 INT
变量,但是也在$minSpeed
和 $maxSpeed
中添加了一个范围。如果用户答案不在可接受的值的范围内,结果将得到 FALSE
。随后可以测试这个答案并且把正确答案返回给用户。在这种情况下,我们将直言不讳。
|
我们已经遵从逻辑测试成功地测试了传入数据,并且向用户显示了正确结果或者一些负面的反馈。不管怎样,我们已经使用了过滤器来确保为输入提供了正确答案。