Session ViewState Cookie Cache -1

Session

背景

由于HTTP协议是无状态的(stateless),服务器会把每一次的HTTP请求当作一次单独的请求,如此服务也不知道之前的请求中的一些常见变量,比如用户信息,应用中的常见变量等。而在Web应用中,我们通常需要知道访问的用户的一些用户信息。基于这样的场景,Session State identifiers 就出现了,她可以标记同一个浏览器发起的请求,并在一段时间里作为session保存,而session中我们就可以实现一些变量的在这段时间里的持久化了。而正因为标记的是浏览器,所以我们在服务器端进行操作的时候,访问的session就是这个对应浏览器的,换言之,就是这个用户的。

替代方案

当然除了session,还可以用其他的一些缓存保存临时变量:

  • Application State,可以存储在同一个应用中所有用户都可以访问的变量
  • Profile Properties,可以在保存一些用户数据,且无时间限制
  • ASP.NET Cache,可以将数据保存在内存中,在同一个服务器下的所有应用均可以访问。从而减少IO的访问量,提高访问速度
  • ViewState 可以将数据保存在当前页面中,不能跨页面访问
  • Cookies 可以将数据保存在浏览器中,通常用于保存一些浏览记录
  • Http Request URL和HTML Form 也可以保存一些参数,用于校验或者页面参数

Code

对Session进行赋值

1
2
Session["FirstName"] = userInfo.Account
Session["LastName"] = userInfo.UserName

Session 中的变量可以实例化成.net 中的任何类型。不过当你要进行实例化的时候,首先确定类型是否合适

1
2
UserInfo userInfo = (ArrayList)Session["UserInfo"];
Session["UserInfo"] = userInfo;

Session Identifiers

在背景中,解释了Session是由唯一的SessionID进行标记的,同时服务器也根据SessionID获取对应的Session数据。如果在服务器端的应用中启用了Session,那么每一次的页面请求服务器都会校验这个请求中的SessionID,如果这个请求中没有SessionID,在ASP.NET中会分配一个新的Session,同时将对应的SessionID返回给浏览器。通常,这个SessionID会被保存到Cookie中。如果是Configure中设置了CookieLess,这个SessionID插入请求的url中。

在之后的请求中,SessionID没有超时就会认定为仍是活动(Active)状态,如果超时了,就会再次新建一个Session。

无论在Cache还是在URL请求中,Session都是明文保存的,恶意用户一旦获取到,而同时session中又有一些敏感性信息,然后就尴尬了。这时候可以在Browser和Server之间用SSL进行加密。

CookieLess SessionIDs

通常SessionID保存在Cookie中,但是也可以通过在Web.Config文件中对CookieLess属性进行配置,配置节信息如下

1
2
3
4
5
6
<configuration>
<system.web>
<sessionState cookieless="true"
regenerateExpiredSessionId="true" />
</system.web>
</configuration>

当对Cookieless属性进行配置之后,ASP.NET会自动将SessionID插入到页面的URL中

1
http://www.example.com/(S(lit3py55t21z5v55vlm25s55))/orderform.aspx

但是保存到URL中后,如果用户手动更改URL,服务器就无法解析到这个SessionID,如此,一个新的Session又会因为这个请求创建。

Regenerating Expired Session Identifiers

SessionID 在Cookieless Sessions 的设置下是有回收机制的,SessionID在这样的情况下可能会被多个浏览器使用,因为URL可能会被各种途径传递。当然也可以配置SessionID不回收,不过这里就需要配置regenerateExpiredSessionId的属性值为true。这样如果SessionID过期了,就会重新生成一个新的SessionID

如果一个请求用一个过期的SessionID进行HTTP POST请求,所有的POST 请求中的数据都会丢失,因为过期的缘故,ASP.NET会对这个请求进行重定向。

对Session Identifiers进行自定义

通过建立一个继承自SessionIDManager class,同时重写CreateSessionID和Validate方法的自定义类对sessionId进行验证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Configuration;
using System.Web.Configuration;
using System.Web;
using System.Web.SessionState;

namespace Samples.AspNet.Session
{

public class GuidSessionIDManager : SessionIDManager
{

public override string CreateSessionID(HttpContext context)
{
return Guid.NewGuid().ToString();
}


public override bool Validate(string id)
{
try
{
Guid testGuid = new Guid(id);

if (id == testGuid.ToString())
return true;
}
catch
{
WriteLog(Some Information);
DoSomeActions();
throw CustomException();
}

return false;
}
}
}

也可以通过ISessionIDManager替代SessionIDManager,比如在非ASP.NET页面中通过使用ISAPI filter。如果服务器是Cookieless的,这时候就需要集成其他的方法,生成URL了。

Session的模式

ASP.NET session state中支持对session中的变量有几种不同的存储模式,通常会保存在对应的进程中,也可以保存在一个单独的进程中, 或者SQLSERVER数据库,甚至是自定义的数据源中。当然,如果应用中,不想有Session,也可以在设置Session Mode的状态为off

Session中的事件

Session_OnStart 一个session创建时发生
Session_OnEnd 一个session被销毁或者过期时发生,然而不是在inProc条件下,这个事件是不支持的。

如果需要定义Session事件,需要在Global.asax中添加对应的代码。

由于Session State的状态和Global.asax文件和WebConfig文件紧密相关,发生修改,可能会导致Session State的丢失,需要严防各类病毒对这两个文件内容的修改。

Session State配置

Session state在Web.Config文件中配置在System.web配置节下。
而sessionState元素中有以下参数需要配置

  • 存储Session的方式
  • sessionId在browser和server之间的传递方式
  • 过期时间
  • 根据Mode状态的选用配置参数
    1
    2
    3
    4
    5
    6
    <sessionState mode="SQLServer"
    cookieless="true "
    regenerateExpiredSessionId="true "
    timeout="30"
    sqlConnectionString="Data Source=MySqlServer;Integrated Security=SSPI;"
    stateNetworkTimeout="30"/>

并发请求和Session State

ASP.NET Session对每一个Session都是独立的,这就意味着两个不同的用户进行并发请求的时候,Session分配是并发的。但是如果两个并发请求用了同一个SessionID,第一个请求就得到获取Session中数据的唯一权限。第二个请求执行只能等到第一个请求完成之后,即第一个请求因为说时间超时释放唯一锁。如果EnableSessionState 的值在page中设置为ReadOnly。只读请求会等待读写请求锁之后。

ViewState

背景

和Session设计的原因有些类似,ViewState也是由于WebApplication 是stateless的才设计出来的。不同的是,Session是对于整个Browser和Server之间的交流,而保存一些常用的数据。而对于ViewState,则是因为需要保存页面和控件的一些信息,页面和控件的信息可能每一次交互结束之后,都会丢失掉。而微软为了克服这个限制,就在ASP.NET框架加入了ViewState的设计。当HTML页面渲染好之后,页面的state信息会进行base64的编码存入到隐藏字段或字段中,从而可以通过ViewState这个属性进行获取。

应用场景

ViewState 是对于单页信息的存储,ViewState用来持久化页面进行交互的时候的各种数据。例如下拉控件的联动效果。

  • 作为session和user profile的替代方案
  • 可以创建View State Provider从而将SQL SERVER或者其他存储作为ViewState存储

什么时候用ViewState进行存储

  • ViewState是不能跨页面访问的,如果需要跨页面访问打算持久化的数据,这个时候,最好使用 ApplicationState,Session,Profile Properties 这样的存储设计
  • ViewState中最好存储过多的数据,否则将会影响到页面的性能
  • 如果在隐藏字段中的数据量过大,一些网关和防火墙也会拒绝授权访问。而防火墙和网关对与隐藏字段的最大数据量的限制是根据不同服务器的需求状况进行集成的。通常状况中,我们需要对MaxPageStateFieldLength属性进行赋值,页面请求发送到服务器的时候,便会对这个请求中的隐藏字段进行分区,保证隐藏字段的大小在服务器限制的大小之下。
  • ViewState是默认使用的,如果有的控件每次请求都需要刷新,这时候可以将ViewState的状态置为off,从而减少ViewState的大小
  • 某些移动端设备不支持隐藏字段,ViewState在这些设备中也就无法使用

Control State

而除了ViewState,ASP.NET也支持Control State。与ViewState类似

Saving Vlues in View State

如果要用ViewState这个属性,ASP.NET page必须要有个form 元素同时还有属性 runat=”server”

使用方法如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
// Sample ArrayList for the page.
ArrayList PageArrayList;

ArrayList CreateArray()
{
// Create a sample ArrayList.
ArrayList result = new ArrayList(4);
result.Add("item 1");
result.Add("item 2");
result.Add("item 3");
result.Add("item 4");
return result;
}

void Page_Load(object sender, EventArgs e)
{
if (ViewState["arrayListInViewState"] != null)
{
PageArrayList = (ArrayList)ViewState["arrayListInViewState"];
}
else
{
// ArrayList isn't in view state, so it must be created and populated.
PageArrayList = CreateArray();
}
// Code that uses PageArrayList.
}

void Page_PreRender(object sender, EventArgs e)
{
// Save PageArrayList before the page is rendered.
ViewState.Add("arrayListInViewState", PageArrayList);
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>View state sample</title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>

ViewState中支持的数据类型

  • Strings
  • Integers
  • Boolean
  • Array
  • ArrayList
  • HashTable
  • Custom Type converters

Reading values from ViewState

1
2
3
4
5
arrayList = new ArrayList();
arrayList = (ArrayList)ViewState["arrayListInViewState"];

this.GridView1.DataSource = arrayList;
this.GridView1.DataBind();

由于无法判断ViewState中是否存在该值,所以需要对这个key值进行是否为空判断

1
2
if (ViewState["color"] == null)
// No such value in view state, take appropriate action.

Securing ViewState

viewstate中的数据一般是在base64编码之后保存在隐藏字段中。而且还会使用MAC(Machine Authentication code)对数据进行编码,哈希值和编码后的数据保存在page中。当页面回发请求到服务器的时候,ASP.NET Framework会重新计算hash-value,同时与ViewState中的进行比较,如果不匹配,就会抛出异常,ViewState中保存的数据便是无效的。

通过创建哈希值,ASP.NET 页框架可以测试视图状态数据是否已被损坏或篡改。但是,即使视图状态数据未被篡改,这些数据仍然可能被恶意用户截获和读取。

使用 MAC 计算视图状态哈希值
用于计算视图状态哈希值的 MAC 密钥可以自动生成,也可以在 Machine.config 文件中指定。如果该密钥是自动生成的,则基于计算机的 MAC 地址(它是该计算机中网络适配器的唯一 GUID 值)进行创建。

恶意用户很难根据视图状态中的哈希值进行反向工程处理以推断出 MAC 密钥。因此,MAC 编码是一种用来确定视图状态数据是否已更改的相当可靠的方式。

通常,用于生成哈希的 MAC 密钥越大,不同字符串的哈希值相同的可能性就越小。如果密钥是自动生成的,则 ASP.NET 使用 SHA-1 编码来创建一个大型密钥。不过,在网络场环境中,所有服务器的密钥必须相同。如果密钥不同,那么当页回发至创建该页的服务器之外的其他服务器时,ASP.NET 页框架将引发异常。因此,在网络场环境中,应在 Machine.config 文件中指定密钥,而不是让 ASP.NET 自动生成密钥。在这种情况下,请确保您创建的密钥足够长,以便使哈希值具有充分的安全性。但是,密钥越长,创建哈希所需要的时间也就越多。因此,必须在安全需求与性能需求之间进行权衡。

加密视图状态
虽然 MAC 编码有助于防止篡改视图状态数据,但这种编码也会妨碍用户查看数据。可以通过下面两种方式来防止他人查看此数据:通过 SSL 传输页,以及加密视图状态数据。要求通过 SSL 发送页有助于防止那些原本不应该收到该页的人探查数据包和未经授权访问数据。

但是,请求页的用户仍然能够查看视图状态数据,因为 SSL 会解密页以便在浏览器中进行显示。如果您不担心授权用户可以访问视图状态数据,则这种方法很好。但在某些情况下,控件可能会使用视图状态存储任何用户都不应访问的信息。例如,页可能包含一个数据绑定控件,该控件存储视图状态的项标识符(数据密钥)。如果这些标识符中包含敏感数据(如客户 ID),则应对视图状态数据进行加密来替代通过 SSL 发送页,或是将其作为通过 SSL 发送页的补充方法。

若要加密数据,请将页的 ViewStateEncryptionMode 属性设置为 true。在视图状态中存储信息时,可以使用常规的读写技术;页会为您处理所有加密和解密工作。对视图状态数据进行加密可能会影响应用程序的性能。因此,如不需要,请不要使用加密。

控件状态加密
使用控件状态的控件可以通过调用 RegisterRequiresViewStateEncryption 方法来要求对视图状态进行加密。如果页中的任何控件都要求对视图状态进行加密,则该页中的所有视图状态都会进行加密。

基于每个用户的视图状态编码
如果网站需要对用户进行身份验证,则可以设置 Page_Init 事件处理程序中的 ViewStateUserKey 属性,以便将页的视图状态与特定用户相关联。这将有助于防止一键式 (one-click) 攻击,在这种方式的攻击中,恶意用户创建一个有效的预先填充的网页,该网页具有来自以前创建的网页的视图状态。攻击者随后引诱受害者单击一个链接,该链接使用受害者的标识向服务器发送页。

如果设置了 ViewStateUserKey 属性,将使用攻击者的标识来创建原始页的视图状态的哈希。受害者被引诱重新发送此页时,由于用户密钥不同,因此哈希值也将不同。这样,页的验证将失败,并且引发一个异常。

必须将 ViewStateUserKey 属性与每个用户的一个唯一值(如用户名或标识符)相关联。

在共享宿主环境中保护配置的安全性
在共享的宿主环境中,恶意用户可能会修改状态管理属性,从而可能影响到计算机上的其他应用程序。修改方式包括:直接修改 Machine.config;使用配置类进行修改;以及使用其他管理和配置工具进行修改。您可以通过对配置文件的节进行加密来帮助防止他人修改您的应用程序配置。有关更多信息,请参见使用受保护的配置加密配置信息

Author: Black_Jack
Link: https://foryl.github.io/blog/2018/05/03/Cache/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.