Chapter 4. 证实所有的输入

 

Wisdom will save you from the ways of wicked men, from men whose words are perverse...

 Proverbs 2:12 (NIV)
Table of Contents
命令行
环境变量
文件描述符
文件内容
CGI输入
其它输入
字符编码
限制合法的输入时间和负载水平

某些输入来自不可信的用户,所以在使用前那些输入必须被证实(过滤)。你可以决定什么是合法的,并拒绝不符合定义的东西。千万不要反其道而行(标明哪些是非法的并拒绝它们),因为很有可能忘记处理某个重要的情况。限制最大字符长度(如果合适的话,还有最小的长度),确定超过该长度时不会失去控制(参见缓存溢出一节以了解更多内容)。

对于字符串,识别合法字符或合法模式(如正规表达式),并拒绝不匹配的东西。当字符串中包含控制字符(特别是换行符或NIL)或shell转义字符时,会有些特殊问题;最好在接受输入过程中遇到转义字符时直接“忽略”这些字符,这样这些字符被不会被意外地发送出去。CERT在处理这种情况时更保守一些,他们推荐忽略所有不属于非忽略字符列表的字符[CERT 1998, CMU 1998],参见下面“限制呼出为合法值”一节以了解更多信息。

限制数字在最小(通常为0)和最大允许值之间。文件名需要检查;通常不把“..”(上一级目录)作为合法值。对文件名而言,最好禁止改变目录,比如不把“/”包含在合法字符集中。完全的email地址检查实际上非常复杂,因为存在大量已有格式,如果要完全支持它们,合法性检查非常复杂;如果必须进行这样的检查,请参见mailaddr(7)和IETF RFC 822 [RFC 822]以了解更多信息。

这些测试一般应集中放在一起,便于今后检验合法性测试的正确性。

要保证合法性测试确实是正确的;特别是在检查的输入会被另一个程序使用的情况下(如文件名、email地址或URL)。这些测试经常出现细微的错误,产生所谓的“代理问题”(检查程序与实际使用数据的程序所用的假设有差异)。

在解析用户输入时,临时放弃所有特权是个好主意。特别是在解析工作复杂(比如使用类lex或类yacc工具),或者编程语言无法防御缓存溢出(如C和C++)的情况下。参见下面最小化许可一节。

下面的几个小节讨论程序的不同类型的输入;注意,输入包括进程状态,如环境变量、umask值,等等。不是所有的输入都在非可信用户的控制之下,所以只需要关注那些受非可信用户控制的输入。

命令行

很多程序使用命令行作为输入接口,通过传递参数接收输入。setuid/setgid程序的命令行接口是由某个非可信用户提供的,所以必须保护好自己。用户对命令行有很大的控制权(通过调用诸如execve(3)之类的调用)。因此,setuid/setgid程序必须证实命令行的输入,不可相信命令行参数0(用户可以把它设为包括NULL的任意值)报告的程序名称。