17.7 视图状态
视图状态是ASP.NET页中的存储库,可以存储必须在回发过程中保留的值。默认情况下,ASP.NET页框架使用视图状态在往返过程之间保存页和控件值。在呈现页的HTML时,必须在回发过程中保留的页和值的当前状态将被序列化为Base64编码字符串。然后,它们将被放入页中的一个或多个隐藏字段。
之后,可以在代码中使用页的ViewState属性访问视图状态,ViewState属性是一个包含键/值对(其中包含视图状态数据)的字典。因此,可以在自己的应用程序中使用视图状态完成以下工作:
1)在各个回发之间保存值,而不将这些值存储在会话状态或用户配置文件中。
2)存储定义的页或控件属性的值。
3)创建一个自定义视图状态提供程序,以便将视图状态信息存储在SQL Server数据库或其他数据存储区中。
17.7.1 写入和读取视图状态
默认情况下,ASP.NET通过ViewState自动保存控件的状态。因此,可能也发现了文本框中的数据在页面提交后还是存在的。
下面的示例演示如何实现从其控件的ViewState属性存储和检索值的Text属性,如下面的代码所示:
public String Text
{
get
{
object o=ViewState["Text"];
return(o==null)?String.Empty:((sring)o;
}
set
{
ViewState["Text"]=value;
}
}
当然,也可以利用ViewState来保存一些程序需要的数据。ViewState中的数据默认是使用base64进行编码的,因此,用户不能直接看到里面的数据。如下面的代码所示:
ViewState["book"]="易学C#";
打开页面,观察源代码,ViewState就在这里:
<input type="hidden"name="VIEWSTATE"id="VIEWSTATE"
value="/wEPDwUKMjA3NjE4MDczNg8WAh4EYm9vawUI5piT5a2mQyNkZKhQ001i
mw03OpvNFxazBv9ppZVE+aMlKoMNfbgU6qek"/>
既然ViewState是存在页面上的,那么ViewState肯定是不能跨页面使用的,而且每个用户访问到的ViewState都是独立的。此外,ViewState也没有什么声明周期的概念,页面在,ViewState就在,页面关闭了,ViewState当然也就关闭了。
下面的示例将以两种不同的形式将视图状态输出,如下面的代码所示:
protected void Page_Load(object sender, EventArgs e)
{
ViewState["book"]="易学C#";
}
protected void Button1_Click(object sender, EventArgs e)
{
Response.Write("ViewState[\"book\"]:<br/>"
+ViewState["book"].ToString()+"<hr/>");
Response.Write("Request[\"__VIEWSTATE\"]:<br/>"
+Encoding.UTF8.GetString(
Convert.FromBase64String(Request["__VIEWSTATE"])));
}
示例运行结果如图17-11所示。
图 17-11 示例运行结果
从图7-11中可以看出,ASP.NET首先对ViewState中的数据进行序列化,然后再使用bas e 6 4编码后存储在页面的隐藏域中。base64不是什么加密算法,只是一种编码算法,任何人都能对base64进行反编码。
最后,需要特别说明的是,可以将这些类型的对象存储到视图状态中:字符串、整数、Boolean值、Array对象、ArrayList对象、哈希表与自定义类型转换器。当然,还可以存储其他类型的数据,但是必须使用Serializable特性编译类,以便可以为视图状态序列化该类的值。
17.7.2 保证视图状态的安全
如果需要在ViewState中保存一些相对比较机密的数据(当然,非常机密的数据不建议保存在ViewState中),又如何保证ViewState的安全性呢?一般来说,可以从以下几个方面入手:
1)使用MAC计算视图状态哈希值。用于计算视图状态哈希值的MAC密钥既可以自动生成,也可以在Machine.config文件中指定。如果该密钥是自动生成的,则基于计算机的MAC地址(它是该计算机中网络适配器的唯一GUID值)进行创建。恶意用户很难根据视图状态中的哈希值进行反向工程处理以推断出MAC密钥。因此,MAC编码是一种用来确定视图状态数据是否已更改的相当可靠的方式。
通常,用于生成哈希的MAC密钥越大,不同字符串的哈希值相同的可能性就越小。如果密钥是自动生成的,则ASP.NET使用SHA-1编码来创建一个大型密钥。不过,在网络场环境中,所有服务器的密钥必须相同。如果密钥不同,那么当页回发至创建该页的服务器之外的其他服务器时,ASP.NET页框架将引发异常。因此,在网络场环境中,应在Machine.config文件中指定密钥,而不是让ASP.NET自动生成密钥。在这种情况下,请确保创建的密钥足够长,以便使哈希值具有充分的安全性。但是,密钥越长,创建哈希所需要的时间也就越多。因此,必须在安全需求与性能需求之间进行权衡。
2)加密视图状态。虽然MAC编码有助于防止篡改视图状态数据,但却它无法阻止用户查看数据。这时候,可以通过SSL传输页,以及对视图状态数据进行加密来阻止非法用户查看数据。它有助于防止那些原本不应该收到该页的人探查数据包和未经授权访问数据。
但是,请求该页的用户仍然能够查看视图状态数据,因为SSL会解密该页以便在浏览器中显示它。如果不担心授权用户可以访问视图状态数据,那么这种方法很适合你。但在某些情况下,控件可能会使用视图状态存储任何用户都不应访问的信息。例如,页可能包含一个数据绑定控件,该控件存储视图状态的项标识符(数据密钥)。如果这些标识符中包含敏感数据(如客户ID),则应对视图状态数据进行加密来替代通过SSL发送页,或将其作为通过SSL发送页的补充方法。
若要加密数据,请将页的ViewStateEncryptionMode属性设置为true。在视图状态中存储信息时,可以使用常规的读写技术;页会处理所有加密和解密工作。对视图状态数据进行加密可能会影响应用程序的性能。因此,如不需要,请不要使用加密。
3)控件状态加密。使用控件状态的控件可以通过调用RegisterRequiresViewStateEncryption方法来要求对视图状态进行加密。如果页中的任何控件都要求对视图状态进行加密,则该页中的所有视图状态都会进行加密。
4)基于每个用户的视图状态编码。如果站点需要对用户进行身份验证,那么,这时候就需要设置Page_Init事件处理程序中的ViewStateUserKey属性,以便将页的视图状态与特定用户相关联。这将有助于防止一键式攻击,在这种方式的攻击中,恶意用户创建一个有效的预先填充的网页,该网页具有来自以前创建的网页的视图状态。攻击者随后引诱受害者单击一个链接,该链接使用受害者的标识向服务器发送页。
如果设置了ViewStateUserKey属性,将使用攻击者的标识来创建原始页的视图状态的哈希。受害者被引诱重新发送此页时,由于用户密钥不同,因此哈希值也将不同。这样,页的验证将失败,并且引发一个异常。
最后,还需要说明一点的是,必须将ViewStateUserKey属性与每个用户的一个唯一值(如用户名或标识符)相关联。
17.7.3 视图状态的优点与局限性
从上面的阐述中,可以看出视图状态具有许多的优点。主要表现在以下几方面:
1)不需要任何服务器资源,视图状态包含在页代码内的结构中。
2)实现简单,视图状态无须使用任何自定义编程。
3)增强的安全功能。
视图状态中的值经过哈希计算和压缩,并且针对Unicode实现进行编码,其安全性要高于使用隐藏域。
当然,除了上面这些优点之外,视图状态也存在着一些局限性:
1)由于视图状态存储在页本身,因此如果存储较大的值,用户显示页和发布页时的速度可能会减慢,尤其是对于移动设备,其带宽通常是有限的。如果隐藏字段中的数据量过大,则某些代理和防火墙将禁止访问包含这些数据的页,而移动设备可能没有足够的内存容量来存储大量的视图状态数据。
2)视图状态存储在页上的一个或多个隐藏域中。虽然视图状态以哈希格式存储数据,但它可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,这导致潜在的安全性问题。
17.7.4 ViewStateMode
除此之外,在ASP.NET4中可以使用ViewStateMode属性来启用单个控件的视图状态。其中,ViewStateMode属性有三个选项值。
❑Inherit:从父Control继承ViewStateMode的值。
❑Enabled:启用此控件的视图状态,即使父控件已禁用了视图状态也是如此。
❑Disabled:禁用此控件的视图状态,即使父控件已启用了视图状态也是如此。
例如,如果要禁用某页的视图状态,然后为该页上的特定控件再将其启用。那么必须将该页和该控件的EnableViewState属性设置为true,将该页的ViewStateMode属性设置为Disabled,然后将该控件的ViewStateMode属性设置为Enabled。
默认情况下,ViewStateMode属性对于页面的默认值为Enabled,而对页面中Web服务器控件的ViewStateMode属性的默认值为Inherit。因此,如果不在页面或控件级别设置ViewStateMode属性,EnableViewState属性的值将确定视图状态行为。
仅当EnableViewState属性设置为true时,页面或控件的ViewStateMode属性才起作用。如果EnableViewState属性设置为false,则即使ViewStateMode属性设置为Enabled,视图状态也将关闭。