利用ASP.NET的内置功能抵御Web攻击(二)

2017 年 12 月 5 日3730


在与当前页相关联的视图状态变量中将一个标识符分配给单个用户

除了有些累赘,这个句子的意思相当清楚;但是,您能老老实实地告诉我,它说明了该属性原本的用途吗?要理解 ViewStateUserKey 的角色,您需要继续往下读,直到 Remarks 部分。

该属性有助于防止一次单击攻击,因为它提供了附加的输入以创建防止视图状态被篡改的哈希值。换句话说,ViewStateUserKey 使得黑客使用客户端视图状态的内容来准备针对站点的恶意张贴困难了许多。可以为该属性分配任何非空的字符串,但最好是会话 ID 或用户的 ID。为了更好地理解这个属性的重要性,下面我们简短介绍一下一次单击攻击的基本知识。

一次单击攻击包括将恶意的 HTTP 表单张贴到已知的、易受攻击的 Web 站点。之所以称为“一次单击”,是因为它通常是以受害者不经意的单击通过电子邮件发送的或者在拥挤的论坛中浏览时发现的诱惑性链接而开始的。通过点击该链接,用户无意中触发了一个远程进程,最终导致将恶意的 <form> 提交到一个站点。大家都坦白些吧:您真能告诉我,您从未因为好奇而单击过 Click here to win $1,000,000 这样的链接吗?显然,并没有什么糟糕的事情发生在您身上。让我们假定的确是这样的;您能说 Web 社区中的所有其他人都幸免于难了吗?谁知道呢。

要想成功,一次单击攻击需要特定的背景条件:

• 攻击者必须充分了解该有漏洞的站点。这是可能的,因为攻击者可以“勤奋地”研究该文件,或者他/她是一位愤怒的内部人员(例如,被解雇而又不诚实的雇员)。因此,这种攻击的后果可能是极其严重的。

• 站点必须是使用 Cookie(如果是持续性 Cookie,效果更好)来实现单次登录,而攻击者曾经收到过有效的身份验证 cookie。

• 该站点的某些用户进行了敏感的事务。

• 攻击者必须能够访问目标页。


前已提及,攻击包括将恶意的 HTTP 表单提交到等待表单的页。可以推知,该页将使用张贴来的数据执行某些敏感操作。可想而知,攻击者清楚地了解如何使用各个域,并可以想出一些虚假的值来达到他的目的。这通常是目标特定的攻击,而且由于它所建立的三角关系,很难追本溯源 — 即黑客诱使受害者单击该黑客站点上的一个链接,而这又会导致恶意代码被张贴到第三个站点。(请参阅图 1。)




图 1. 一次单击攻击
为什么是不抱怀疑的受害者?这是因为,这种情况下,服务器日志中所显示的发出恶意请求的 IP 地址,是该受害者的 IP 地址。如前所述,这种工具并不像“经典”的 XSS 一样常见(和易于发起);但是,它的性质决定了它的后果可能是灾难性。如何应对它?下面,我们审视一下这种攻击在 ASP.NET 环境下的工作机理。

除非操作编码在 Page_Load 事件中,否则 ASP.NET 页根本不可能在回发事件之外执行敏感代码。要使回发事件发生,视图状态域是必需的。请牢记,ASP.NET 会检查请求的回发状态,并根据是否存在 _VIEWSTATE 输入域,相应地设置 IsPostBack。因此,无论谁要向 ASP.NET 页发送虚假请求,都必须提供一个有效的视图状态域。

一次单击攻击要想得手,黑客必须能够访问该页。此时,有远见的黑客会在本地保存该页。这样,他/她就可以访问 _VIEWSTATE 域并使用该域,用旧的视图状态和其他域中的恶意值创建请求。问题是,这能行吗?

为什么不能?如果攻击者可以提供有效的身份验证 cookie,黑客就可以进入,请求将被照常处理。服务器上根本不会检查视图状态内容(当 EnableViewStataMac 为 off 时),或者只会检查是否被篡改过。默认情况下,试图状态中没有机制可以将该内容与特定的用户关联起来。攻击者可以轻松地重用所获取的视图状态,冒充另一个用户合法地访问该页,以生成虚假请求。这正是 ViewStateUserKey 介入的地方。
如果选择准确,该属性可以将用户特定的信息添加到视图状态。处理请求时,ASP.NET 会从视图状态中提取秘钥,并将其与正在运行的页的 ViewStateUserKey 进行比较。如果两者匹配,请求将被认为是合法的;否则将引发异常。对于该属性,什么值是有效的?

为所有用户将 ViewStateUserKey 设置为常量字符串,相当于将它保留为空。您必须将它设置为对各个用户都不同的值 — 用户 ID,会话 ID 更好些。由于一些技术和社会原因,会话 ID 更为合适,因为会话 ID 不可预测,会超时失效,并且对于每个用户都是不同的。

以下是一些在您的所有页中都必不可少的代码:

void Page_Init (object sender, EventArgs e) {
ViewStateUserKey = Session.SessionID;
:
}

为了避免重复编写这些代码,您可以将它们固定在从 Page 派生的类的 OnInit 虚拟方法中。(请注意,您必须在 Page.Init 事件中设置此属性。)

protected override OnInit(EventArgs e) {
base.OnInit(e);
ViewStateUserKey = Session.SessionID;
}

总体说来,使用基 page 类始终都不失为一件好事,我在 Build Your ASP.NET Pages on a Richer Bedrock 一文中已经进行了说明。如果您要了解更多有关一次单击攻击者的伎俩的信息,可以在 aspnetpro.com 找到一篇非常好的文章。


Cookie 和身份验证
Cookie 之所以存在,是因为它们可以帮助开发人员达到一定目的。Cookie 充当了浏览器与服务器之间的一种持续性链接。特别是对于使用单次登录的应用程序来说,失窃的 cookie 正是使得攻击成为可能的罪魁祸首。这对于一次单击攻击来说一点没错。

要使用 Cookie,无需以编程方式显式创建和读取它们。如果您使用会话状态且实现表单身份验证,您会隐式地使用 Cookie。当然,ASP.NET 支持无 cookie 的会话状态,而且,ASP.NET 2.0 还引入了无 cookie 的表单身份验证。因此,理论上您可以在没有 Cookie 的情况下使用这些功能。我并不是说您不再必须这么做了,但事实上这正是疗法比疾病更糟的情形之一。无 Cookie 的会话,实际上将会话 ID 嵌入了 URL 中,这样谁都可以看到。

与使用 Cookie 有关的潜在问题有哪些?Cookie 可能被盗(即被复制到黑客的计算机)和投毒(即被填充以恶意数据)。这些操作通常是即将发起的攻击的前奏。如果被盗,Cookie 会“授权”外部用户以您的名义连接到应用程序(并使用受保护的页),这可能使黑客轻松地规避授权,并能够执行角色和安全设置所允许受害者执行的任何操作。因此,身份验证 Cookie 通常被赋予相对较短的生存期,即 30 分钟。(请注意,即使浏览器的会话完成所需的时间更长,cookie 仍会过期。)发生失窃时,黑客有 30 分钟的时限来尝试攻击。

可以将这个时限加长,以免用户不得不过于频繁地登录;但请注意,这么做会将您自己置于危险境地。任何情况下,都应避免使用 ASP.NET 持续性 Cookie。它将导致 cookie 具有几乎永久的生存期,最长可达 50 年!下面的代码片段演示了如何轻松修改 cookie 的过期日期。

void OnLogin(object sender, EventArgs e) {
// Check credentials
if (ValidateUser(user, pswd)) {
// Set the cookie's expiration date
HttpCookie cookie;
cookie = FormsAuthentication.GetAuthCookie(user, isPersistent);
if (isPersistent)
cookie.Expires = DateTime.Now.AddDays(10);
// Add the cookie to the response
Response.Cookies.Add(cookie);
// Redirect
string targetUrl;
targetUrl = FormsAuthentication.GetRedirectUrl(user, isPersistent);
Response.Redirect(targetUrl);
}
}

您可以在自己的登录表单中使用这些代码来微调身份验证 Cookie 的生存期。

gigi_miao

本文来源:ASPcool

责任编辑:王晓易_NE0011

0 0