正文

[转]安全编程: 验证输入--接收用户数据的最佳实践2009-03-04 14:52:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/lym51/41230.html

分享到:

本文介绍了如何验证输入――任何安全程序的首要环节之一。 2003 年 7 月,计算机应急反应小组协调中心报告了 Microsoft Windows 的 DirectX MIDI 库中一组危险的漏洞。DirectXMIDI 库是用于播放 MIDI 格式音乐的底层 Windows 库。不幸的是,这个库没有能力去检查 MIDI 文件中的所有数据值;text、copyright 或者 MThd track 域中错误的值可以导致这个库的失效,而攻击者就可以利用这一漏洞让系统去执行他们想要执行的任何代码。这是特别危险的,因为 Internet Explorer 在察看一个包含 MIDI 文件链接的网页时,会自动加载那个文件并播放它。结果呢?一个攻击者只需要发布一个网页,当用户察看这个网页时,让用户的计算机删除所有的文件、把所有的机密文件通过电子邮件发送到其他地方、机器崩溃,或者去做任何攻击者想要做的事情。 检查输入 在几乎所有安全的程序中,您的第一道防线就是检查您所接收到的每一条数据。如果您能不让恶意的数据进入您的程序,或者至少不在程序中处理它,您的程序在面对攻击时将更加健壮。这与防火墙保护计算机的原理很类似;它不能预防所有的攻击,但它可以让一个程序更加稳定。这个过程叫做检查、验证或者过滤您的输入。 一个明显的问题是,在何处执行检查?是在数据最初进入程序时,或者是在一个低层次的例程在实际使用这些数据时?通常,最好在这两处都对其进行检查;这样,即使一个攻击者成功地突破了一道防线,他们还会遇到另一条。最重要的规则是所有的数据必须在使用之前被检查。 误区:寻找不正确的输入 安全程序开发人员一个最大的误区是尝试去查找“非法的”数据值。这是不对的,因为攻击者非常聪明;他们常常会想到出其他的危险数据值。所以应该做的是确定哪些是合法的,检查数据是否符合定义,拒绝所有不符合定义的数据。为了安全,在开始时应该特别谨慎,只允许您知道合法的数据。毕竟,如果您限制的过于严格,用户很快就会报告说程序不允许合法的数据进入。另一方面,如果你限制的过于宽松,可能得直到程序被破坏您才会发现这一问题。 例如,我们假设您要基于用户的某个输入创建文件名。您可能知道不应该允许用户的输入中包括“/”,但是仅仅去检查这一个字符可能是不对的。比如,控制字符呢?空格会不会出问题?如果以破折号开头呢(在不好的代码中可能会出问题)?特别的短语会不会出问题?在绝大多数情况下,如果您创建了一个“非法”字符的列表,攻击者还是可以找到利用您的程序的方法。所以,应该检查并保证输入符合你认为是安全的特定模式,而拒绝不符合这个模式的所有输入。 确定出您所知道的危险值仍不失为一个好主意:您可以用它们(在头脑中)检查您的确认例程。这样,如果您知道使用“/”是危险的,就可以检查您的模式保证它不会让这个字符通过。 当然,所有这些都面临着一个问题:什么是合法的值?答案部分取决于您所期望的数据类型。所以接下来的几节我们将讨论程序要用到的几种通用数据类型――以及如何处理它们。 数字 我们从看起来最容易读的一类信息开始――数字。如果您期望输入的是一个数字,就确认数据是数字格式――比如,只是针对阿拉伯数字,并且是至少一位阿拉伯数字(您可以使用与正则表达式 ^[0-9]+$ 检查它)。在大多数情况下会有一个最小值和一个最大值;如果是这样,要确认数据在合法范围之内。 不要根据没有减号这一条件就认为不会有负数。在很多数据读取例程中,如果读到一个特别大的数,就会发生"溢出"而变成一个负数。实际上,一个非常聪明的针对 Sendmail 的攻击正是基于这一原理。Sendmail 会检查"调试标记"是不是比合法的值大,但是它并没有去检查这个值是不是负数。Sendamil 的开发者想当然地认为既然他们不允许使用减号,就不必再去检查输入是不是负数了。问题是数据读取例程会将大于 2^31 的数,比如4,294,967,269 ,转换成负数。攻击者可以利用这一点来覆盖至关重要的数据,并控制 Sendmail。 如果您读取的浮点数,还有另外需要关注的问题。许多设计用来读取浮点数的例程可能会允许“NaN”(非数字)这样的值。这样实际上会给接下来的处理例程带来问题,因为任何与这些数据比较的结果都会是假(而且,NaN 与 NaN 也不相等!)。您还需要知道标准 IEEE 浮点数的其他特殊定义,比如正无穷大和负无穷大,负零(还有正零)。所有您的程序没有考虑到的输入数据都有可能导致以后被利用。 字符串 同样,对于字符串您也要确定哪些是合法的,并拒绝所有其他的字符串。通常指定合法字符串最简单的方法是使用正则表达式:只需正确使用正则表达式编写描述哪些字符串合法的模式,抛弃那些不符合这个模式的数据。例如, ^[A-Za-z0-9]+$ 指定字符串至少为一个字符长,而且只能包括大写字母、小写字母和阿拉伯数字0到9(任意的顺序)。您可以使用正则表达式来更为详细地限制所允许的字符串(例如,您可以进一步指定第一个字符可以是哪些字母)。所有的语言都已实现正则表达式的库;Perl 是基于正则表达式的,对于 C,函数 regcomp(3) 和 regexec(3) 是POSIX.2 标准,并被广泛应用。 如果您使用正则表达式,一定要明确地指出您要匹配数据的的开始(通常用 ^ 来标识)和结束(通常用 $ 来标识)。如果您忘记了包括 ^ 或者 $ ,攻击者就可以在他们的攻击中嵌入合法的文本通过您的检查。如果您使用的 Perl,并且使用的它的多行选项( m ),要注意:您必须使用 \A 来标识开始,用 \Z 来标识结束,因为多行操作改变了 ^ 和 $ 的含义。 最大的问题是如何明确地指出在字符串中哪些是合法的。通常,您应该尽可能地严格。有很多字符都会带来特定的问题;只要可能,您就不愿意允许在程序内部或者最终输出中有特定含义的那些字符。人们发现这确实很困难,因为在一些情况下有太多的字符可能会带来问题。 这里是经常会带来问题的字符的部分清单: * 常规控制字符(字符值小于32):还特别包括字符0,传统上称做 NUL;我把它称为 NIL 以区别于 C 语言中的 NULL 指针。在 C 语言中 NIL 标记了一个字符串的结束;即便您没有直接使用 C 语言,许多库会间接地去调用 C 语言的例程,如果给出了 NIL,就有可能出错。另一个问题可以被解释为命令结束的行结束符。不幸的是,有好几种行结束编码:基于 UNIX 的系统使用的是换行字符 (0x0a),但是基于 DOS 的系统(包括windows)使用的是 CP/M 的回车换行 (0x0d 0x0a),Apple MacOS 使用的是回车 (0x0d),许多 IBM 主机(比如 OS/390)使用的是下一行 (0x85),并且有一些程序甚至(错误地)使用反 CP/M 标记 (0x0a 0x0d)。 * 字符值大于127:这些是国际化的字符,但问题是它们可能会有许多可能的含义,所以您需要确保它们被正确地解释。通常这些都是 UTF-8 编码的字符,有其自身的复杂性;可以参考本文 后面关于 UTF-8 的讨论。 * 元字符:元字符是在您所依赖的程序或库中——比如命令 shell 或者 SQL——有特定含义的字符。 * 在您的程序中有特定含义的字符:例如,用于定界的字符。许多程序将数据存放在文本文件中,使用逗号、制表符或者冒号隔开数据域;您需要拒绝含有这些值的数据或者对其进行编码。当前,一个常见的问题是小于号 (

阅读(2448) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册