web应用程序的安全问题是基于这样一种假设——所有用户都是潜在的hacker,他们的任何输入都应该是不被信任的。以该假设为基础便产生了一系列防御机制,这些防御机制在概念上是基本相似的,只是在设计细节和执行效果上会有较大的不同。web应用的防御机制主要由以下核心元素组成:
- 处理用户对程序数据和功能的访问,以阻止未经授权用户的访问
- 处理用户提交到程序的数据,防止非法输入导致程序出错而泄露信息。
- 对于入侵者,要有一套防卫措施来抵御入侵
- 要有专门提供给管理员使用的功能窗口,使管理员能够配置应用程序的功能,监视应用程序的运行状态。
由于以上元素在防御机制中所扮演的核心角色,知己知彼方能百战不殆,彻底的了解这些防御机制是成功入侵的前提(prerequisite)。无论菜鸟还是老鸟,在入侵前花点时间来了解一下可能会遇到的这些防御机制的工作原理,快速识别出其弱点和漏洞,就很有必要了。
一、处理用户的访问
基本上任何一款应用都需要处理用户对其数据和功能的访问,典型场景如不同的用户策略:匿名用户,普通用户和管理员用户,他们所能访问到的功能和数据应该是有差别的。大多数应用程序是通过以下三步(trio)的安全机制来处理用户的访问:
- 认证(Authentication)
- 会话管理(Session Management)
- 访问控制(Access Control)
以上每个机制有代表了一个应用程序攻击界面(Attack Surface)重要方面,由于他们之间相互依赖的关系,那么其整体的安全性就取决于这一系列安全机制中最弱的那一环的安全性,其中任何一个环节的缺陷都会最终导致attacker可以不经限制的访问应用程序的功能和数据。
认证(Authentication)
认证是处理用户访问的基本环节,大多数当今的web应用程序都提供了一种传统的认证模型,即用户提交其用户名和密码,并由应用程序检查其合法性。
在一些安全性较高的场合,比如网上银行,可能还包含了其他更屌炸的多重认证方式,如数字证书,U盾,智能卡,手机令牌(Challenge-Response tokens)等。除了用户登录,认证机制通常还提供了其他支持功能,如注册,账户恢复,更改密码等。尽管认证机制表面上看起来很简单,但在其设计和实现上经常会遭受很多缺陷。通常问题是attacker能够识别用户的用户名并暴力破解其密码,或者能够利用其逻辑缺陷绕过登录功能限制。
会话管理(Session Management)
处理用户访问的另一个任务就是处理已认证用户的session。当成功登录到应用程序中后,用户就可以使用其浏览器,通过向服务器发送一系列的HTTP请求来访问应用的各种页面和功能。与此同时,服务器上的应用程序会接收到不同用户发送过来的无数请求,其中有些是认证过的用户的,有些是匿名用户的。为加强访问控制的效果,web应用需要一种方式来识别和处理这些来自不同用户的一系列请求。
事实上当今所有的web应用都能满足这一需要,通过为每个独立用户创建一个session,并发送给用户一个token作为令牌。session本身是一个存储在服务器端的数据结构,用来跟踪用户与应用程序之间的交互状态,token是应用程序用来与session建立映射的唯一的字符串。当用户收到一个token之后,web浏览器就会在之后的每次HTTP请求中添加上该token,应用程序在每次的HTTP请求中抽取出token值,与服务器上的token-session映射表对比后就能识别出每个用户。HTTP Cookie是用来传递session-token的标准方式,而许多应用程序也通过使用隐藏表单域或者在URL的查询字符串中传递token。并且,如果一个用户在一定长度的时间段内未在发送HTTP请求的话,session就会变成过期状态,需要用户重新登录。
就攻击界面(attack surface)而言,会话管理机制的安全性主要取决于其token的安全性。大多数的攻击行为都是通过窃取正常登录用户的token来实现的。若窃取成功的话,那么attacker就可以伪装成(masquerade)该用户来访问应用程序上的各个功能。该领域的主要漏洞,一是在token的生成上的缺陷,使得attacker可以通过猜测或穷举发送给用户的token,二是在应用程序在之后处理token上的缺陷,使得attacker能够捕获其他用户的token。
另外有一小部分应用程序省掉了对会话token的需要,而是通过其他方式的多重请求来重新识别用户。比如使用HTTP内置的认证机制,浏览器就能自动的在每次请求中提交用户的认证信息使应用程序直接使用这种方式来验证用户。还有一些方式,如应用程序在客户端存储用户的状态信息,而不是存储在服务端,通常这种方式是以加密表单的形式以防止篡改。
访问控制(Access Control)
处理用户访问的最后一个逻辑步骤就是对于每个独立的请求,做出并执行正确的决定,该请求究竟是该允许还是拒绝。若该功能能够正常实现的,那么应用程序就能够知道究竟哪个用户的请求可以被接受。因此,应用程序需要确定该用户是否已经授权来执行某些动作,或者访问某些请求访问的数据。
访问控制机制通常需要实现一些更细微的逻辑处理,如对应用程序的不同区域要有不同的考虑以及实现不同类型的功能。一个应用程序可能支持大量的用户角色,每一个都涉及到各种特定权限的集合,每个独立用户可能只被允许访问应用程序所有数据中的一个子集,每个功能都可能涉及到事物控制和检测,而所有这些功能的正确执行都要基于用户的不同身份。
正是由于访问控制需求上的逻辑复杂性,该机制经常成为安全漏洞的根源,使得attacker能够未经授权的访问数据和功能。开发人员设想的用户与应用程序之间的交互经常存在漏洞,并且经常在一些功能里忘记访问控制的检查。检查这种漏洞通常是很费力的,因为本质上对功能上的每一项都需要做重复的同样的检查。访问控制漏洞如今依然很流行,因此在入侵一个web应用程序前花点时间尝试一下还是值得的。后面章节会讲到如何能够自动的进行一些访问控制检测。
二、处理用户的输入
回忆一下前面提到的安全问题的本质:所有用户的数据输入都不能信任。大量的对web应用程序的入侵都涉及到非法数据的输入,通过这些精心构造的数据,让应用程序产生一些原程序涉及人员未曾设想到的行为。因此,web应用的安全防御机制中的一个非常重要的环节,就是对用户输入数据的处理。
基于输入的漏洞可以出现在web应用的功能的任何地方,并且涉及到现在常用的每一项技术领域。输入验证(Input Validation)常是抵御这种攻击的必要防御技术。然而,现在尚未有一种能够部署到任何地方的保护措施;并且,抵御恶意输入的攻击也不像它听起来那么简单直接。
输入的多样性(Various of Input)
一个典型的web应用程序通常使用许多不同的表单来处理用户提供的数据。有一些输入验证可能对于一些表单来说就不是那么合适了。
在有些应用场合里,应用程序可以对输入数据的每个字符都进行非常严格的(stringent)合法性检查,比如在登录窗口的用户名输入框中,可能就限制用户的输入最大长度,并且要求输入的只能是英文字符。
其他情况下,应用程序可能无法对输入数据进行严格检验,比如,提交到个人详细信息页面的地址栏可能允许输入字母,数字,空格,连字符,逗号以及其他字符。但是对这些内容来讲,应用程序依然可以施加一些限制,比如输入数据的长度要在合理范围内(比如不超过50个字符)并且不能包含HTML标记
在有些场合,应用程序必须接受一些来自用户的任意输入。比如用户使用blog应用来发布一篇有关application hacking的博文,那么博文的内容以及评论可能就会包含很多明显的涉及到入侵的行为的字符串。应用程序需要将这些内容存储到数据库里,写到磁盘上,最后以安全的方式展示给用户浏览,而不能简单的拒绝用户对此类数据的输入。
除了用户通过浏览器输入的各种数据外,还有一些存储在服务器上,并发送给客户端的数据,比如cookie和隐藏表单域等。这些数据对普通用户来讲当然是不可见的,但对一些有经验的入侵者来讲是很容易来查看和修改的。在这种情况下,应用程序就需要对接受到的数据进行严格的检查了,比如一些参数只能是已知的特定的值,或者必须遵循一个特定的格式。此外,当应用程序检测到一些程序生成的数据被人为修改过了,而对于使用标准浏览器的普通用户来讲这些数据时不可能被修改的,那么这通常就表明有用户在尝试寻找该程序中的漏洞。在这种情况下,应用程序应该拒绝该请求并记录该事件以备调研。