C5:验证所有输入
该控制措施的新版本已发布!
您正在查看OWASP十大主动控制措施的2018年旧版。您可以在OWASP十大主动控制措施 2024版中的C3:验证所有输入并处理异常中找到有关相同控制的信息!
描述
输入验证是一种编程技术,用于确保只有格式正确的数据才能进入软件系统组件。
语法和语义有效性
应用程序在使用数据之前(包括将其显示给用户),应检查数据是否同时具有语法和语义有效性(按此顺序)。
语法有效性指数据符合预期格式。例如,应用程序可能允许用户选择一个四位数的“账户ID”来执行某种操作。应用程序应假定用户正在输入SQL注入有效载荷,并应检查用户输入的数据是否长度恰好为四位数字,并且只包含数字(此外还要使用适当的查询参数化)。
语义有效性仅接受在给定应用程序功能和上下文中可接受范围内的输入。例如,在选择日期范围时,开始日期必须早于结束日期。
白名单 vs 黑名单
进行输入语法验证通常有两种通用方法,即允许列表和拒绝列表
- 拒绝列表或拒绝列表验证试图检查给定数据是否不包含“已知恶意”内容。例如,Web应用程序可能会阻止包含精确文本
<SCRIPT>
的输入,以帮助防止XSS。然而,这种防御可以通过小写或大小写混合的脚本标签绕过。 - 允许列表或允许列表验证试图检查给定数据是否符合一组“已知良好”规则。例如,美国州份的允许列表验证规则是仅限于有效美国州份的2个字母的代码。
重要提示 在构建安全软件时,推荐使用允许列表作为最低限度的方法。拒绝列表容易出错,可以通过各种规避技术绕过,并且单独依赖时可能很危险。尽管拒绝列表通常可以被规避,但它通常有助于检测明显的攻击。因此,虽然允许列表通过确保数据的正确语法和语义有效性来帮助限制攻击面,但拒绝列表有助于检测并可能阻止明显的攻击。
客户端和服务器端验证
出于安全考虑,输入验证必须始终在服务器端进行。虽然客户端验证对于功能性和某些安全目的可能有用,但它通常很容易被绕过。这使得服务器端验证对安全性而言更加基础。例如,JavaScript验证可能会提醒用户某个特定字段必须由数字组成,但服务器端应用程序必须验证提交的数据是否仅包含该功能所需数值范围内的数字。
正则表达式
正则表达式提供了一种检查数据是否符合特定模式的方法。让我们从一个基本示例开始。
以下正则表达式用于定义一个允许列表规则,以验证用户名。
^[a-z0-9_]{3,16}$
此正则表达式仅允许小写字母、数字和下划线字符。用户名长度也限制在3到16个字符之间。
注意:拒绝服务的可能性
创建正则表达式时应谨慎。设计不当的表达式可能导致潜在的拒绝服务条件(又称ReDoS)。各种工具可以测试验证正则表达式是否不容易受到ReDoS攻击。
注意:复杂性
正则表达式只是实现验证的一种方式。对于某些开发人员来说,正则表达式可能难以维护或理解。其他验证替代方案包括以编程方式编写验证方法,这对于某些开发人员来说可能更容易维护。
输入验证的局限性
输入验证并不总是能使数据“安全”,因为某些形式的复杂输入可能“有效”但仍然危险。例如,一个有效的电子邮件地址可能包含SQL注入攻击,或者一个有效的URL可能包含跨站脚本攻击。除了输入验证之外,还应始终对数据应用额外的防御措施,例如查询参数化或转义。
验证序列化数据的挑战
某些形式的输入过于复杂,以至于验证只能最低限度地保护应用程序。例如,反序列化不受信任的数据或可被攻击者操纵的数据是危险的。唯一安全的架构模式是不接受来自不受信任源的序列化对象,或者仅限于简单数据类型进行有限的反序列化。您应避免处理序列化数据格式,并尽可能使用更容易防御的格式,例如JSON。
如果不可能,则在处理序列化数据时考虑一系列验证防御措施。
- 实施序列化对象的完整性检查或加密,以防止恶意对象创建或数据篡改。
- 在对象创建之前的反序列化期间强制执行严格的类型约束;通常代码期望一组可定义的类。已证明存在绕过此技术的案例。
- 隔离反序列化代码,使其在极低权限的环境中运行,例如临时容器。
- 记录安全反序列化异常和失败,例如传入类型不是预期类型,或反序列化抛出异常的情况。
- 限制或监控来自反序列化容器或服务器的入站和出站网络连接。
- 监控反序列化,如果用户持续进行反序列化则发出警报。
意外用户输入(批量赋值)
一些框架支持将HTTP请求参数自动绑定到应用程序使用的服务器端对象。这种自动绑定功能可能允许攻击者更新原本不应被修改的服务器端对象。攻击者可能利用此功能修改其访问控制级别或绕过应用程序预期的业务逻辑。
这种攻击有多种名称,包括:批量赋值、自动绑定和对象注入。
举一个简单的例子,如果用户对象有一个名为privilege
的字段,用于指定用户在应用程序中的权限级别,恶意用户可以查找修改用户数据的页面,并在发送的HTTP参数中添加privilege=admin
。如果以不安全的方式启用自动绑定,则代表用户的服务器端对象将被相应修改。
可以使用两种方法来处理此问题
- 避免直接绑定输入,而是使用数据传输对象(DTO)。
- 启用自动绑定,但为每个页面或功能设置白名单规则,以定义允许自动绑定的字段。
更多示例可在OWASP 大量赋值(Mass Assignment)速查表中找到。
验证和清理HTML
考虑一个需要接受用户HTML输入的应用程序(通过将内容表示为HTML的WYSIWYG编辑器或直接接受HTML输入的特性)。在这种情况下,验证或转义将无济于事。
- 正则表达式不足以理解HTML5的复杂性。
- 对HTML进行编码或转义将无济于事,因为它会导致HTML无法正常渲染。
因此,您需要一个能够解析和清理HTML格式文本的库。请参阅关于HTML净化(HTML Sanitization)的XSS防范速查表以获取更多关于HTML净化的信息。
库和框架中的验证功能
所有语言和大多数框架都提供验证库或函数,应利用它们来验证数据。验证库通常涵盖常见数据类型、长度要求、整数范围、“是否为空”检查等等。许多验证库和框架允许您定义自己的正则表达式或逻辑进行自定义验证,以便程序员可以在整个应用程序中利用此功能。验证功能的示例包括PHP的过滤器函数或Java的Hibernate Validator。HTML净化器的示例包括Ruby on Rails的sanitize方法、OWASP Java HTML Sanitizer或DOMPurify。
防止的漏洞
- 输入验证减少了应用程序的攻击面,有时可以使针对应用程序的攻击变得更加困难。
- 输入验证是一种为特定形式的数据提供安全性、针对特定攻击的技术,不能可靠地作为通用安全规则应用。
- 输入验证不应作为防止XSS、SQL注入及其他攻击的主要方法。