5.4 安全处理客户端数据

如前所述,由于客户端组件和用户输入不在服务器的直接控制范围内,Web应用程序的核心安全面临威胁。客户端及其提交的所有数据从本质上讲都不值得信任。

5.4.1 通过客户端传送数据

许多应用程序之所以存在缺陷,是因为它们通过客户端以危险的方式传送产品价格和折扣率之类的重要数据。

如果可能,应用程序应完全避免通过客户端传送这类数据。在几乎任何一种可能出现的情况下,都可以将这类数据保存在服务器上,并在必要时通过服务器端逻辑直接引用。例如,接受用户购买各种产品而提交的订单的应用程序应允许用户提交产品代码和数量,并在服务器端数据库中查询每一种产品的价格。用户没有必要向服务器提交产品价格。即使应用程序向不同的用户提供不同的价格或折扣,也不必抛弃这种模型。价格可按用户分类保存在数据库中,而折扣率则保存在用户资料或会话对象中。应用程序已经拥有计算某一特殊用户所购买的某种产品的价格所需的一切信息——在不安全的模型中,它必须(除非无法做到)将这个价格保存在一个隐藏的表单字段中。

如果开发者认为他们别无选择,只有通过客户端传送重要数据,那么应当对数据进行签名与/或加密处理以防止用户篡改。采用这种操作还必须避免两个重要的威胁。

img002 签名或加密数据可能易受重传攻击(replay attack)。例如,如果在将价格保存到隐藏表单之前对其进行加密,攻击者就可以用一个更加便宜的产品的加密价格代替最初的产品价格。为防止这种攻击,应用程序需要在加密数据中包含足够的上下文,以防止攻击者在另一种情况下重新传送产品价格。例如,应用程序可以将产品代码和价格组合在一起,将得到的字符串单独加密,然后确认随订单提交的加密字符串是否与被订购的产品完全匹配。

img002 如果用户知道并/或能够控制送交给他们的加密字符串的明文值,那么他们就可以实施各种密码攻击,找出服务器使用的加密密钥。之后,他们就能够用密钥加密任意值,完全避开解决方案提供的保护。

对于在ASP.NET平台上运行的应用程序而言,建议决不要将任何定制数据以及任何你不希望在屏幕上向用户显示的敏感数据保存在ViewState中。应总是激活用于启用ViewState MAC的选项。

5.4.2 确认客户端生成的数据

从理论上讲,客户端无法安全确认由客户端生成并且向服务器传送的数据。

img002 可轻易避开HTML表单字段和JavaScript之类的轻量级客户端控件,无法保障服务器收到的输入的安全性。

img002 在浏览器扩展组件中执行的控件有时更难以避开,但这种控件只能暂时阻止攻击者入侵。

img002 使用经强化模糊处理或压缩的客户端代码增添了另一层障碍,但是,蓄意攻击者还是能够克服这些障碍。(其他领域的类似处理是使用DRM技术防止用户复制数字媒体文件。许多公司在客户端控件上投入大量资金,但每一种新型解决方案通常在不久后就被攻破。)

确认客户端生成数据的唯一安全方法是在应用程序的服务器端实施保护。客户端提交的每一项数据都应被视为危险和潜在恶意的。

img003  错误观点  有时候,人们认为使用任何客户端控件必然会造成不利影响。一些专业渗透测试员甚至把使用客户端控件看作是一个“重大发现”,并不检验服务器是否使用这些控件或者使用它们是否出于非安全考虑。实际上,尽管本章描述的各种攻击能够产生严重的安全警告,但在许多情况下使用客户端控件并不会造成任何安全漏洞。

img002 客户端脚本可用于确认输入,以提高可用性,避免与服务器来回通信。例如,如果用户输入的出生日期格式不正确,通过客户端脚本向他们提出警报可提供更加无缝的使用体验。当然,应用程序必须对之后提交给服务器的数据进行重新确认。

img002 有些时候,客户端数据确认可以与安全措施一样有效——例如,通过它防御基于DOM的跨站点脚本攻击。但是,许多时候攻击的直接目标是另一名应用程序用户,而不是服务器端应用程序。而且,利用潜在的漏洞不一定需要向服务器传送任何恶意数据。请参阅第12章和第13章了解有关这种情形的详细内容。

img002 如前所述,有许多方法可通过客户端传送加密数据,而不会遭到破坏或重传攻击。

5.4.3 日志与警报

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

img001  注解  有些时候,虽然用户的浏览器禁用JavaScript,但他们仍然能够使用采用JavaScript的应用程序。出现这种情况,主要是因为浏览器完全忽略了基于JavaScript的表单确认代码,提交的是用户输入的原始信息。为避免错误警报,日志与警报机制应了解这种情况会在什么地方出现,会如何发生。