Web安全(4)

避开客户端控件

由于客户端可提交任意输入,Web应用程序的核心安全因此受到威胁。大部分Web应用程序仍然依靠在客户端采取各种措施,对它提交给服务器的数据进行控制。这种做法会导致一个基本的安全缺陷:用户能够完全控制客户端和由其提交的数据,并可以避开任何在客户端执行但服务器不采用的控件。
应用程序依靠客户端控件限制用户输入表现在两个方面:

  1. 应用程序可通过客户端控件,使用某种它认为可防止用户修改的机制传送数据
  2. 应用程序在客户端执行保护措施,控制用户和客户端的交互,从而对功能进行限制,并(或)在用户输入之前对这些输入进行控制

通过客户端传送数据

应用程序通常以终端用户(最后使用这个产品的用户)无法直接查看或修改的方式向客户端传送数据,希望客户端在随后的请求中将这些数据送回服务器。

隐藏表单字段

  • 隐藏HTML表单字段是一种表面上看似无法修改,通过客户端传送数据的常用机制。
  • 如果一个表单标记为隐藏,它就不会显示在屏幕上,但是,用户提交表单时,保存在表单中的字段名称和值仍被传送给应用程序

例子

典型的表单如下

创建该表单的HTML代码如下

<form method="post" action="shop.aspx?prod=1">
    Product:HuaWei x30<br/>
    Price: 3300<br/>
    Quantity:<input type="text" name="quantity"><br/>
    <input type="hidden" name="price" value="3300">
    <input type="submit" value="Buy">
    </form>

注:表单字段名为price,其被标记为隐藏(hidden),用户提交表单时,这个字段将被交送给服务器:

POST /SHOP/shop.aspx?pord=1 HTTP/1.1
Host: mdsec.net
Content-Type: Application/x-www-form-urlencoded
Content-Length: 20

quantity=1&price=3300

虽然price字段并未显示在输入框里,用户无法对其进行编辑,但这只是因为应用程序指示浏览器隐藏该字段而已。

要实现编辑操作,可以对HTML源码进行修改,然后重新载入页面,但是,使用拦截代理服务器对数据进行动态修改更为方便。

HTTP cookie是通过客户端传送数据的另一种常用机制,HTTP cookie一般并不显示在屏幕上,也不可由用户直接修改。用户可以通过拦截代理服务器,通过更改设置cookie的服务器响应或随后发布这些cookie的客户端请求,对HTTP cookie进行修改。

例子

消费者登录应用程序后,收到以下响应

HTTP/1.1 200 OK
Set-Cookie: DiscountAgreed=25
Content-Length: 1530
...

DiscountAgreed cookie是依靠客户端控件(基于cookie一般无法被修改这个事实)保护通过客户端传送的数据的典型示例,如果应用程序信任DiscountAgreed cookie返回给服务器的值,那么消费者就可以修改这个值获得任意折扣

POST /shop/shop.aspx?pord=3 HTTP/1.1
Host: mdsec.net
cookie: DiscountAgreed=15
Content-Length: 10

quantity=1

URL参数

应用程序常常通过预先设定的URL参数通过客户端传送数据。如果包含参数的URL显示在浏览器的地址栏里,任何用户不需要任何工具就可以修改URL的参数

Referer消息头

浏览器在大多数HTTP请求中使用Referer消息头,浏览器使用这个消息头指示提出当前请求的页面的URL(或是因为用户单击了一个超链接或提交了一个表单,或者是因为该页面引用了其他资源)。因此,我们可以利用这个消息头向客户端传送数据,这是因为应用程序处理的URL受其控制。

实例

忘记密码的用户向服务器提出重制密码的请求

GET /auth/CreateUser.ashx HTTP/1.1
Host: mdsec.bet
Referer: https://mdsec.net/auth/Admin.ashx

应用程序可以使用Referer证实这个请求是在正确的阶段(Adimin.ashx)提出的,然后才允许用户访问请求的功能(这里指重置密码)。但是,因为用户控制着每个HTTP请求,包括HTTP消息头,他可以进入CreateUser.ashx并使用拦截服务器将Referer消息头的值改为应用程序需要的值,从而轻易避开这种控制

模糊数据

对传送的数据进行加密或进行了某种形式的模糊处理。

ASP.NET ViewState

  • ASP.NET ViewState是一种通过客户端传送模糊数据的常用机制。
  • 它是一个由所有ASP.NET Web应用程序默认创建的隐藏字段,其中包含关于当前页面状态的序列化信息。
  • 服务器通过它在连续提交请求的过程中保存用户界面中的元素,而不需要在服务器端维护所有状态信息。
  • ViewState的参数实际上是一个Base64编码字符串。
  • ASP.NET平台通过在ViewState中加入一个密钥散列(称为MAC保护)来防止篡改。

收集用户数据:HTML表单

  • HTML表单是一种最简单、最常用的机制,它用于从用户收集输入并将其提交给服务器。
  • 这种方法的基本应用:用户在已命名的字段中输入数据,再将它们以名/值对的形式提交给服务器。

长度限制

<form method="post" action="shop.aspx?pord=1">
Quantity: <input type="text" name="quantity" maxlength="1"></br>
</form>

上述HTML语句规定quantity字段的最大长度为1,因此,浏览器将阻止用户在输入字段中输入任何超过一个字符的值,而服务器端引用程序也认为它收到的quantity参数将小于10.但是,通过拦截提交表单的请求,并在其中输入任意值,或拦截包含表单的响应,并删除maxlength属性,就可以轻易避开这种限制。

基于脚本的确认

通过Javascript对用户输入进行限制。

禁用元素

如果HTML表单中的一个元素被标记为禁用,它会在屏幕上出现,但以灰色显示,并且无法像常规空间那样编辑或使用。提交表单时,表单也不会向服务器传送这个元素。

<form>
Price: <input type="text" disabled="true" name="price" value="200">
</form>

收集用户数据:浏览器扩展

另一种确认并提交用户数据的主要方法是使用在浏览器扩展中运行的客户端组件(如Java/Flash)

  • 浏览器扩展可以通过输入表单,或者在某些情况下通过与客户端操作系统的文件系统或注册表交互,以各种不同的方式收集数据。
  • 在将收集到的数据传给服务器之前,它们可以对这些数据执行任何复杂的确认和处理

常见的浏览器扩展技术

常见的浏览器扩展技术包括:Java applet/Flash/Silverlight,它们类似的安全功能包括:

  • 它们均编译成中间字节码
  • 它们在提供沙盒(sandbox,是一种安全机制,为运行中的程序提供隔离环境)执行环境的虚拟机中运行
  • 它们可能会使用远程框架,这类框架采用序列化来传输复杂数据结构,或通过HTTP传送对象。

Java

java applet在Java虚拟机(JVM)中运行,并采用由Java安全策略应用的沙盒。

Flash

Flash对象在Flash虚拟机中运行。和java applet一样,Flash也要在主机上的沙盒中运行

Silverlight

Silverlight是微软开发的与Flash类似的产品。该产品主要运用于启动各种桌面应用程序,允许Web应用程序在浏览器内的沙盒环境中提供精简的.NET体验。

攻击浏览器扩展的方法

  • 拦截并修改浏览器扩展组件提出的请求及服务器响应,但可能会遇到数据模糊处理或对技术方案进行了序列化的问题
  • 直接对组件实施攻击,并尝试反编译它的字节码,以查看其源代码;或使用调试器与组件进行动态交互

拦截浏览器扩展的流量

处理序列化数据

  • 序列化:数据序列化就是将对象或者数据结构转化成特定的格式
  • 应用程序可能会首先对数据或对象进行序列化处理,然后通过HTTP请求传送这些数据或对象。通常而言,需要对序列化数据进行解压缩才能了解这些数据。可以根据所采用的客户端组件推断出相关数据的序列化格式,但是,任何时候,仔细检查相关HTTP消息才能确认序列化格式
java序列化

java语言本身支持对象的序列化。而且,java applet可能会以这种方式在客户端与服务器应用程序组件之间传送序列化数据结构。通常序列化java对象会使用Content-Type消息头:

Content-Length: application/x-java-serialized-object
Flash序列化

Flash使用自己的可用于在服务器和客户端组件之间传输复杂数据结构的序列化格式,通常可以通过以下Content-Type消息头辨别动作信息格式(AMF):

Content-Type: application/x-amf
Silverlight序列化

Silverlight应用程序能够利用.NET平台内置的Windows通信基础(WCF)远程框架。使用WCF的Silverlight客户端组件通常采用微软的用于SOAP的.NET二进制格式,可以通过Content-Type消息头辨别该格式:

Content-Type: application/soap+msbin1

拦截浏览器扩展流量时遇到的障碍

如果浏览器已设置成使用拦截代理服务器,代理服务器可能并不会拦截,或无法拦截浏览器扩展组件提出的请求。之所以出现这种情况,可能是由于组件的HTTP代理或SSL出现问题。产生这个问题的原因主要有:

  1. 客户端组件可能并不执行在浏览器或计算机的设置中指定的代理配置,这是因为组件可能会在浏览器本身或扩展框架提供的API以外发出它们自己的HTTP请求。
  • 解决方案:修改计算机的hosts文件,同时将代理服务器配置为支持匿名代理,并自动重定向到正确的主机
  1. 客户端组件可能不接受代理服务器提供的SSL证书
  • 解决方案: 将代理服务器配置为使用一个主CA证书(用于为访问的每个站点的每台主机签署有效的证书),并在计算机的可信证书库中安装该CA证书。
  1. 客户端组件使用除HTTP以外的协议进行通信,而拦截代理服务器却无法处理这些协议。
  • 解决方案:使用网络嗅探器或功能挂钩工具查看并修改相关流量(比如Echo Mirage)

反编译浏览器扩展

  • 在对浏览器扩展组件实施攻击时,最彻底的方法,是反编译对象、对源代码进行全面分析,修改源代码以该改变对象行为,然后重新编译源代码
  • 字节码:字节码是一种有相关解释器(如Java虚拟机或Flash播放器)执行的、不依赖于特定平台的高级二进制表示形式,每种浏览器扩展技术都使用它们自己的字节码格式,因此,浏览器扩展可在解释器本身可运行的任何平台上运行。

反编译字节码流程

下载字节码

第一步是下载要处理的可执行字节码。一般情况下,字节码会从HTML源代码(运行浏览器扩展的应用程序页面)中指定的URL加载到单独的文件中。Java applet通常使用< applet>标签加载,其他组件则使用< object>标签加载

<applet code="CheckQuantity.class" codebase="/scripts" id="CheckQuantityApplet"></applet>

某些情况下,加载字节码的URL可能并不明显,因为组件可能使用不同浏览器扩展框架提供的各种包装脚本(wrapper script)进行加载。确定字节码的另一种方法,是在浏览器加载浏览器扩展后,在代理服务器历史记录中查找该URL

反编译字节码
  • 字节码通常以独立文件包的形式发布,可能需要进行解压缩才能获得单个字节码文件,然后将其反编译为源代码。
  • 正常情况下,java applet打包成.jar(java档案)文件,Silverlight对象打包成.xap文件。这两种文件均使用zip档案格式。
  • 因此,只需用.zip扩展名重命名这些文件,然后用任何.zip读取器就可以将它们解压缩成单个的文件
  • Java字节码包含在.class文件中,Silverlight字节码包含在.dll文件中。
  •  解压缩相关文件包后,需要反编译这些文件才能获得源代码

注:Flash对象打包成.swf文件,在使用反编译器之前,不需要对这类文件进行压缩处理

反编译字节码工具:

  • java工具:Java字节码可以使用Jad(Java反编译器)的工具反编译成Java源代码
  • Flash工具:Flash可以反编译成ActionScript源代码。另一种更有效的方法,是将字节码反编译成人类可读的格式,而不是将其完全反编译成源代码
  • Silverlight工具:Silverlight字节码可以使用一种称为.NET Reflector的工具反编译成源代码
分析源代码

以下是需要寻找的一些项目:

  • 在客户端发送的输入或其他安全相关逻辑和事件
  • 在向服务器传送数据之前用于包装用户提交的数据的模糊或加密程序
  • 在用户界面中不可见,但可以通过修改组件进行解锁的“隐藏的”客户端功能
  • 对以前未通过解析应用程序确定的服务器端功能的引用
  1. 在浏览器中重新编译执行
    要改变组件的行为,可以对反编译得到的源代码进行修改,重新将其编译成字节码,然后在浏览器中执行修改后的组件。可采用的工具如下:
  • java:使用JDK中的javac程序重新编译修改后的源代码
  • Flash:使用flasm重新汇编修改后的字节码,或使用Adode的某个Flash开发套件重新编译修改后的ActionScript源代码
  • Silverlight:使用Visual Studio 重新编译源代码

注:对于Java和Silverlight,需要用修改后的字节码文件替换已解压的档案中的文件,使用zip实用程度重新压缩这些文件,然后根据需要将文件扩展名改为.jar或.xap

最后需要将修改后的组件加载到浏览器中

  1. 在浏览器以外重新编译并执行
    有时可以使用代理服务器在原始组件提交经过确认的输入时拦截相关请求,并用修改后的组件输出的值替换这些请求。要实施这种攻击,需要对在相关浏览器扩展中运行的原始可执行文件进行修改,将其更改为可以在命令行中运行的独立程序。

  2. 使用JavaScript操纵原始组件
    可以通过修过HTML页面中与组件交互的JavaScript来达到目的。通过查看组件的源代码,可以确定组件的所有可直接从JavaScript调用的公共方法,以及组件处理这些方法的参数的方式。

字节码模糊处理

经过模糊处理的字节码更难反编译,常用的模糊处理技巧如下:

  • 用没有意义的表达式(如a、b、c)代替有意义的类、方法和成员变量名称
  • 用new、int之类的保留关键字代替项目名称。
  • 许多模糊处理工具删除字节码中不必要的调试和元信息,包括源文件名和行号(使栈追踪缺乏信息)、局部变量名称(使调试更麻烦)和内部类信息(使反射无法正常进行)
  • 增加多余代码,以看似有用的方式建立并处理各种数据
  • 使用跳转命令(jump instruction)对整个代码的执行路径进行修改,致使攻击者无法判别源代码的逻辑执行顺序
  • 引用非法的编程结构,如无法到达的语句和缺少return语句的代码路径

附加调试器

由于调试器在字节码级别运行,因此,可以使用调试器轻松控制并了解组件的执行过程。JavaSnoop是一款Java调试器,它能够与Jad集成,以用于反编译源代码、在应用程序中跟踪变量,并在方法中设置断点来查看和修改参数。

本地客户端组件

一些应用程序需要在用户计算机中执行基于浏览器的VM沙盒内无法执行的操作。以下是这些功能的示例:

  • 验证用户是否装有最新的病毒扫描器
  • 验证代理服务器设置及其他企业配置是否有效
  • 集成智能卡读取器

本地客户端组件一般通过ActiveX控件传送,ActiveX控件是在浏览器沙盒以外运行的定制浏览器扩展。由于没有对于的中间字节码,解译本地客户端组件要困难得多,但是,我们在避开客户端控件时采用的方法对本地客户端组件仍然适用。以下是一些工具介绍:

  • OllyDbg是一个可用于遍历本地可执行代码、设置断点,并在磁盘上或在运行时对可执行文件应用补丁的Windows调试器
  • IDA Pro是一个反汇编程序,它可以将大量的平台上的本地可执行代码反汇编成人类可读的汇编代码

安全处理客户端数据

由于客户端组件和用户输入不在服务器的直接控制范围内,所以客户端及其提交的所有数据本质上讲都不值得信任。

通过客户端传送数据

  • 如果可能,应尽量避免从客户端传送重要数据,可以将这些数据保存在服务器上,并在必要时通过服务器端逻辑直接引用。(例如购物程序中,用户可以向服务器提交产品代码和数量,产品价格则可以通过服务器端数据库进行查询,而不需要用户从客户端提交)
  • 如果只有通过客户端传送重要数据才能达到目的,应当对数据进行签名/加密处理以防止用户篡改,但是这种操作还必须避免两个重要的威胁:
  1. 签名或加密数据易受到重传攻击(reply attack)
  2. 如果用户知道或能控制送交给他们的加密字符串的明文值,那么他们就可以实施各种密码攻击,找出服务器使用的加密密钥

确认客户端生成的数据

从理论上讲,客户端无法安全确认由客户端生成并且向服务器传送的数据。确认客户端生成数据的唯一安全方法是在应用程序的服务器端实施保护。客户端提交的每一项数据都应被视为危险和潜在恶意的。

日志和警报

虽然应用程序采用长度限制和基于JavaScript的确认之类的机制来提高性能与可用性,但这些机制应与服务器端侵入检测防御工具组合使用,对客户端提交的数据进行确认的服务器端逻辑应认识到,客户端也采用了同样的确认机制。如果服务器端收到已被客户端阻止的数据,应用程序可能会据此推断,一名用户正设法避开这种确认,因此这些数据可能是恶意的,应用程序应将异常记录到日志中,适当情况下向应用程序管理员发出实时警报,终止用户会话或者暂时冻结其账户。


   转载规则


《Web安全(4)》 fightingtree 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录