第 5 章。验证所有输入

 

智慧必能保你脱离恶道,救你脱离说乖谬话的人...

 箴言 2:12 (NIV)
目录
5.1. 命令行
5.2. 环境变量
5.2.1. 一些环境变量是危险的
5.2.2. 环境变量存储格式是危险的
5.2.3. 解决方案 - 提取和擦除
5.2.4. 不要让用户设置他们自己的环境变量
5.3. 文件描述符
5.4. 文件名
5.5. 文件内容
5.6. 基于 Web 的应用程序输入(尤其是 CGI 脚本)
5.7. 其他输入
5.8. 人类语言(区域设置)选择
5.8.1. 如何选择区域设置
5.8.2. 区域设置支持机制
5.8.3. 合法值
5.8.4. 底线
5.9. 字符编码
5.9.1. 字符编码简介
5.9.2. UTF-8 简介
5.9.3. UTF-8 安全问题
5.9.4. UTF-8 合法值
5.9.5. UTF-8 相关问题
5.10. 防止输入中的跨站恶意内容
5.11. 过滤可能被重新呈现的 HTML/URI
5.11.1. 移除或禁止某些 HTML 数据
5.11.2. 编码 HTML 数据
5.11.3. 验证 HTML 数据
5.11.4. 验证超文本链接 (URI/URL)
5.11.5. 其他 HTML 标签
5.11.6. 相关问题
5.12. 禁止 HTTP GET 执行非查询操作
5.13. 反垃圾邮件
5.14. 限制有效输入时间和负载级别

一些输入来自不可信任的用户,因此这些输入在使用前必须经过验证(过滤)。您应该确定什么是合法的,并拒绝任何不符合该定义的输入。不要反过来做(识别什么是非法的并编写代码来拒绝这些情况),因为您很可能忘记处理非法输入的重要情况。

不过,识别“非法”值有一个很好的理由,那就是作为一组测试(通常只是在您脑海中执行)来确保您的验证代码是彻底的。当我设置输入过滤器时,我会从思想上攻击过滤器,看看是否有非法值可以通过。根据输入的不同,以下是一些常见的“非法”值示例,您的输入过滤器可能需要阻止:空字符串、“.”、“..”、“../”、任何以“/”或“.”开头的内容、任何内部包含“/”或“&”的内容、任何控制字符(尤其是 NIL 和换行符)和/或任何设置了“高位”的字符(尤其是十进制值 254 和 255,字符 133 是 OS/390 使用的 Unicode 行分隔符)。同样,您的代码不应检查“坏”值;您应该在思想上进行此检查,以确保您的模式无情地将输入值限制为合法值。如果您的模式不够窄,您需要仔细重新检查模式,看看是否还有其他问题。

限制最大字符长度(以及适当的最小长度),并确保在超出此类长度时不会失去控制(有关缓冲区溢出的更多信息,请参阅第 6 章)。

以下是一些常见的数据类型,以及您在使用来自不受信任的用户的数据之前应验证的事项

除非您考虑到它们,否则合法字符模式不得包含对程序内部或最终输出具有特殊含义的字符或字符序列

这些测试通常应集中在一个地方,以便以后可以轻松检查验证测试的正确性。

确保您的有效性测试实际上是正确的;当检查将由另一个程序使用(例如文件名、电子邮件地址或 URL)的输入时,这尤其是一个问题。通常,这些测试存在细微的错误,从而产生所谓的“代理问题”(其中检查程序做出的假设与实际使用数据的程序不同)。如果有相关的标准,请查看它,但也搜索以查看程序是否有您需要了解的扩展。

在解析用户输入时,最好暂时放弃所有权限,甚至创建单独的进程(解析器永久放弃权限,另一个进程根据解析器请求执行安全检查)。如果解析任务很复杂(例如,如果您使用类似 lex 或 yacc 的工具),或者如果编程语言不能防止缓冲区溢出(例如,C 和 C++),则尤其如此。有关最小化权限的更多信息,请参阅第 7.4 节

当使用数据进行安全决策时(例如,“让此用户进入”),请务必使用可信通道。例如,在公共 Internet 上,不要仅使用机器 IP 地址或端口号作为验证用户的唯一方法,因为在大多数环境中,此信息可以由(潜在的恶意)用户设置。有关更多信息,请参阅第 7.11 节

以下小节讨论了程序的各种输入类型;请注意,输入包括进程状态,例如环境变量、umask 值等等。并非所有输入都在不受信任的用户的控制之下,因此您只需要担心那些受控制的输入。