跳到内容

输入验证速查表

简介

本文旨在为您的应用程序提供清晰、简单、可操作的输入验证安全功能指导。

输入验证的目标

执行输入验证是为了确保只有格式正确的数据才能进入信息系统的工作流,防止格式错误的数据在数据库中持久化并触发各种下游组件的故障。输入验证应尽可能早在数据流中进行,最好是在从外部方接收到数据后立即进行。

来自所有潜在不可信来源的数据都应进行输入验证,这不仅包括面向互联网的Web客户端,还包括通过外部网络(如来自供应商、合作伙伴、厂商或监管机构)的后端馈送,它们各自都可能被攻破并开始发送格式错误的数据。

输入验证不应作为防止跨站脚本 (XSS)SQL注入以及其他在相关速查表中涵盖的攻击的主要方法,但如果实施得当,它能显著有助于减少这些攻击的影响。

输入验证策略

输入验证应在句法和语义层面同时应用

  • 句法验证应强制执行结构化字段(例如,社会安全号、日期、货币符号)的正确语法。
  • 语义验证应强制执行其在特定业务上下文中的正确性(例如,开始日期在结束日期之前,价格在预期范围内)。

始终建议在处理用户(攻击者)请求时尽早阻止攻击。输入验证可用于在应用程序处理未经授权的输入之前检测到它们。

实施输入验证

输入验证可以使用任何允许有效强制执行句法和语义正确性的编程技术来实现,例如:

  • Web应用框架中原生提供的数据类型验证器(如Django验证器Apache Commons验证器等)。
  • 针对这些格式的输入,根据JSON SchemaXML Schema (XSD)进行验证。
  • 具有严格异常处理的类型转换(例如 Java 中的 Integer.parseInt(),Python 中的 int()
  • 数值参数和日期的最小和最大值范围检查,字符串的最小和最大长度检查。
  • 小范围字符串参数(例如,星期几)的允许值数组。
  • 用于任何其他结构化数据的正则表达式,覆盖整个输入字符串 (^...$),并且使用“任意字符”通配符(例如 .\S
  • 拒绝列表(即拒绝已知危险模式)可以用作额外的防御层,但它应补充而非替代允许列表,以帮助捕获一些常见的攻击或模式,而不应将其作为主要的验证方法。

允许列表与拒绝列表

使用拒绝列表验证来试图检测可能危险的字符和模式,如撇号 ' 字符、字符串 1=1<script> 标签,是一个常见错误,因为攻击者可以轻易绕过此类过滤器。

此外,此类过滤器经常阻止合法的输入,例如 O'Brian,其中 ' 字符完全合法。有关XSS过滤器规避的更多信息,请参阅此维基页面

虽然拒绝列表作为额外的防御层对于捕获一些常见的恶意模式可能有用,但不应将其作为主要方法。允许列表仍然是防止潜在有害输入的更健壮和安全的方法。

允许列表验证适用于用户提供的所有输入字段。允许列表验证涉及精确定义哪些是授权的,根据定义,所有其他内容均未经授权。

如果数据结构良好,例如日期、社会安全号、邮政编码、电子邮件地址等,那么开发人员应该能够定义一个非常强大的验证模式(通常基于正则表达式)来验证此类输入。

如果输入字段来自一组固定的选项,例如下拉列表或单选按钮,那么输入必须与最初提供给用户的值之一精确匹配。

验证自由格式Unicode文本

自由格式文本,尤其是包含Unicode字符的文本,由于需要允许的字符空间相对较大,因此被认为难以验证。

自由格式文本输入也强调了正确进行上下文感知输出编码的重要性,并清楚地表明输入验证不是防止跨站脚本攻击的主要保障措施。如果您的用户想在评论字段中输入撇号 ' 或小于号 <,他们可能有完全合法的理由,而应用程序的任务是在数据的整个生命周期中正确处理这些内容。

自由格式文本输入的主要输入验证手段应为:

  • 规范化:确保在所有文本中都使用规范编码,并且不存在无效字符。
  • 字符类别允许列表: Unicode允许列出诸如“十进制数字”或“字母”等类别,这不仅涵盖了拉丁字母,还包括全球使用的各种其他文字(例如阿拉伯语、西里尔语、中日韩表意文字等)。
  • 单个字符允许列表:如果您在名称中允许字母和表意文字,并且还想允许爱尔兰名称中的撇号 ',但又不想允许整个标点符号类别。

参考资料

正则表达式 (Regex)

开发正则表达式可能很复杂,并且远远超出了本速查表的范围。

互联网上有许多关于如何编写正则表达式的资源,包括此网站OWASP验证正则表达式仓库

在设计正则表达式时,请注意正则表达式拒绝服务(ReDoS)攻击。这些攻击会导致使用设计不当的正则表达式的程序运行非常缓慢,并长时间占用CPU资源。

总而言之,输入验证应:

  • 至少应用于所有输入数据。
  • 定义允许接受的字符集。
  • 定义数据的最小和最大长度(例如 {1,25})。

允许列表正则表达式示例

验证美国邮政编码(5位数字加上可选的-4)

^\d{5}(-\d{4})?$

验证下拉菜单中的美国州选择

^(AA|AE|AP|AL|AK|AS|AZ|AR|CA|CO|CT|DE|DC|FM|FL|GA|GU|
HI|ID|IL|IN|IA|KS|KY|LA|ME|MH|MD|MA|MI|MN|MS|MO|MT|NE|
NV|NH|NJ|NM|NY|NC|ND|MP|OH|OK|OR|PW|PA|PR|RI|SC|SD|TN|
TX|UT|VT|VI|VA|WA|WV|WI|WY)$

Java正则表达式使用示例

使用正则表达式验证参数“zip”的示例。

private static final Pattern zipPattern = Pattern.compile("^\d{5}(-\d{4})?$");

public void doPost( HttpServletRequest request, HttpServletResponse response) {
  try {
      String zipCode = request.getParameter( "zip" );
      if ( !zipPattern.matcher( zipCode ).matches() ) {
          throw new YourValidationException( "Improper zipcode format." );
      }
      // do what you want here, after its been validated ..
  } catch(YourValidationException e ) {
      response.sendError( response.SC_BAD_REQUEST, e.getMessage() );
  }
}

一些允许列表验证器也已在各种开源包中预定义,您可以加以利用。例如:

客户端与服务器端验证

输入验证必须在数据被应用程序功能处理之前在服务器端实现,因为任何在客户端执行的基于JavaScript的输入验证都可能被禁用JavaScript或使用Web代理的攻击者绕过。推荐的方法是同时实施客户端基于JavaScript的验证以改善用户体验,以及服务器端验证以确保安全,充分利用两者的各自优势。

验证富用户内容

验证用户提交的富内容非常困难。更多信息,请参阅XSS速查表中关于使用为该任务设计的库清理HTML标记的部分。

防止XSS和内容安全策略

所有用户控制的数据在HTML页面中返回时都必须进行编码,以防止恶意数据(例如XSS)的执行。例如,<script> 将被返回为 &lt;script&gt;

编码类型取决于用户控制的数据插入到页面的具体上下文。例如,HTML实体编码适用于放置在HTML主体中的数据。然而,放置在脚本中的用户数据将需要JavaScript特有的输出编码。

有关XSS预防的详细信息请参阅:OWASP XSS预防速查表

文件上传验证

许多网站允许用户上传文件,例如个人资料图片或更多。本节旨在帮助安全地提供此功能。

请查阅文件上传速查表

上传验证

  • 使用输入验证来确保上传的文件名使用预期的扩展类型。
  • 确保上传文件的大小不超过定义的最大文件大小。
  • 如果网站支持ZIP文件上传,请在解压文件前进行验证检查。检查内容包括目标路径、压缩级别、预估解压大小。

上传存储

  • 使用新文件名在操作系统上存储文件。不要将任何用户控制的文本用于此文件名或临时文件名。
  • 当文件上传到Web时,建议在存储时重命名文件。例如,如果上传的文件名为test.JPG,则将其重命名为JAI1287uaisdjhf.JPG,使用随机文件名。这样做的目的是防止直接文件访问和使用模糊文件名规避过滤器(例如 test.jpg;.asp/../../../../../test.jpg)的风险。
  • 应分析上传的文件是否存在恶意内容(防恶意软件、静态分析等)。
  • 客户端不应能够指定文件路径;文件路径应由服务器定义。

公开提供上传内容

  • 确保上传的图片以正确的内容类型(例如 image/jpeg, application/x-xpinstall)提供服务。

警惕特定文件类型

上传功能应采用允许列表方法,只允许特定的文件类型和扩展名。然而,重要的是要警惕以下文件类型,如果允许它们,可能会导致安全漏洞:

  • crossdomain.xml / clientaccesspolicy.xml: 允许Flash、Java和Silverlight中的跨域数据加载。如果在带有身份验证的站点上允许这些文件,则可能导致跨域数据窃取和CSRF攻击。请注意,这根据具体插件版本的不同可能会变得相当复杂,因此最好直接禁止名为“crossdomain.xml”或“clientaccesspolicy.xml”的文件。
  • .htaccess.htpasswd: 提供基于目录的服务器配置选项,不应被允许。请参阅HTACCESS文档
  • 建议不允许Web可执行脚本文件,例如 aspx, asp, css, swf, xhtml, rhtml, shtml, jsp, js, pl, php, cgi

图片上传验证

  • 使用图片重写库来验证图片是否有效并去除无关内容。
  • 根据图片处理检测到的图片内容类型,将存储图片的扩展名设置为有效的图片扩展名(例如,不要仅仅信任上传的头部信息)。
  • 确保检测到的图片内容类型在定义的图片类型列表(jpg, PNG等)中。

电子邮件地址验证

句法验证

电子邮件地址的格式由RFC 5321定义,并且比大多数人想象的要复杂得多。例如,以下都被认为是有效的电子邮件地址:

  • "><script>alert(1);</script>"@example.org
  • [email protected]
  • user@[IPv6:2001:db8::1]
  • " "@example.org

使用正则表达式正确解析电子邮件地址以验证其有效性非常复杂,尽管有一些公开可用的正则表达式文档

最大的注意事项是,尽管RFC定义了一种非常灵活的电子邮件地址格式,但大多数实际实现(例如邮件服务器)都使用更受限制的地址格式,这意味着它们会拒绝技术上有效的地址。虽然这些地址可能在技术上是正确的,但如果您的应用程序无法实际向它们发送电子邮件,那么这些地址就没有多大用处。

因此,验证电子邮件地址的最佳方法是执行一些基本的初始验证,然后将地址传递给邮件服务器,如果邮件服务器拒绝则捕获异常。这意味着应用程序可以确信其邮件服务器可以向其接受的任何地址发送电子邮件。初始验证可以像这样简单:

  • 电子邮件地址包含两部分,由 @ 符号分隔。
  • 电子邮件地址不包含危险字符(例如反引号、单引号或双引号,或空字节)。
    • 哪些字符是危险的将取决于地址的使用方式(在页面中回显、插入数据库等)。
  • 域部分只包含字母、数字、连字符 (-) 和点 (.)。
  • 电子邮件地址长度合理
    • 本地部分(@ 前面)不应超过63个字符。
    • 总长度不应超过254个字符。

语义验证

语义验证是关于确定电子邮件地址是否正确和合法的。最常见的方法是向用户发送一封电子邮件,并要求他们点击电子邮件中的链接,或者输入发送给他们的代码。这提供了一个基本的保证,即:

  • 电子邮件地址是正确的。
  • 应用程序可以成功地向其发送电子邮件。
  • 用户可以访问该邮箱。

发送给用户以证明所有权的链接应包含一个令牌,该令牌应:

  • 至少32个字符长。
  • 使用安全的随机数源生成。
  • 一次性使用。
  • 有时限(例如,八小时后过期)。

验证电子邮件地址的所有权后,用户应通过常规机制在应用程序上进行身份验证。

一次性电子邮件地址

在某些情况下,用户在应用程序上注册时可能不想提供真实的电子邮件地址,而是提供一次性电子邮件地址。这些是公开可用的地址,不需要用户进行身份验证,通常用于减少用户主要电子邮件地址收到的垃圾邮件数量。

阻止一次性电子邮件地址几乎是不可能的,因为有大量的网站提供这些服务,并且每天都有新的域名创建。有一些公开可用的列表和已知的商业一次性域名列表,但这些总是会不完整。

如果使用这些列表来阻止一次性电子邮件地址的使用,则应向用户显示一条消息,解释他们为何被阻止(尽管他们很可能只会寻找另一个一次性提供商,而不是提供其合法地址)。

如果必须阻止一次性电子邮件地址,则应只允许来自特定允许的电子邮件提供商的注册。然而,如果这包括Google或Yahoo等公共提供商,用户可以简单地向它们注册自己的一次性地址。

子地址

子地址允许用户在电子邮件地址的本地部分(@ 符号之前)指定一个标签,该标签将被邮件服务器忽略。例如,如果 example.org 域支持子地址,则以下电子邮件地址是等效的:

许多邮件提供商(如Microsoft Exchange)不支持子地址。最著名的支持者是Gmail,尽管还有许多其他提供商也支持。

有些用户会为他们注册的每个网站使用不同的标签,这样如果他们开始收到发送到某个子地址的垃圾邮件,他们就可以识别是哪个网站泄露或出售了他们的电子邮件地址。

由于它可能允许用户使用单个电子邮件地址注册多个账户,一些网站可能希望通过删除 +@ 符号之间的一切内容来阻止子地址。这通常不被推荐,因为它暗示网站所有者要么不知道子地址,要么希望阻止用户在其泄露或出售电子邮件地址时识别他们。此外,通过使用一次性电子邮件地址,或简单地向受信任的提供商注册多个电子邮件账户,可以轻易绕过此限制。

参考资料