由于ASP.NET存在服务器端控件,但是Web又是无状态的,于是就用到了ViewState,将服务器控件状态信息存储到ViewState,通过与服务器交互进行数据的传递。而ViewState其实是存储在hidden中,这样就很容易被非法获取、篡改。
于是今天的引子就出来了,即初步研究下ViewState相关原理机制,然后一定程度解决前文所述问题。
有两篇好文可以先分享出来:
.Net 反序列化之 ViewState 利用 - 安全客,安全资讯平台 (anquanke.com)https://www.anquanke.com/post/id/221630#h2-4ASP.NET 小技巧:重写 ViewState 的存储目的地,以提高页面性能 - 走看看 (zoukankan.com)
http://t.zoukankan.com/RChen-p-338327.html我这里从实用角度做一简化,从代码层面、配置方面简单记录下。
代码层面,ViewState主要就涉及2个Page重写方法:
一个是加载ViewState(来源于客户端)LoadPageStateFromPersistenceMedium()
一个是保存ViewState(然后输出到客户端)SavePageStateToPersistenceMedium(object state)
那么,我们为了防篡改,可以对这两个方法进行重写,对保存ViewState进行加密、对加载ViewState解密处理。
保存ViewState进行加密
/// <summary>
/// 传递到前端执行,进行数据加密
/// </summary>
/// <param name="viewState"></param>
protected override void SavePageStateToPersistenceMedium(object viewState)
{
LosFormatter format = new LosFormatter();
StringWriter writer = new StringWriter();
//序列化
format.Serialize(writer, viewState);
string vsRaw = writer.ToString();
//加密
string vsText = DESCryptoServiceHelper.Encrypt(vsRaw);
Pair pair;
PageStatePersister persister = this.PageStatePersister;
object newViewState;
if (viewState is Pair)
{
pair = (Pair)viewState;
persister.ControlState = pair.First;
newViewState = pair.Second;
}
else
{
newViewState = viewState;
}
//重新保存
persister.ViewState = vsText;
persister.Save();
}
加载ViewState解密
/// <summary>
/// 后台加载的时候调用,需要先解密
/// </summary>
/// <returns></returns>
protected override object LoadPageStateFromPersistenceMedium()
{
PageStatePersister persister = this.PageStatePersister;
persister.Load();
if (persister != null)
{
string viewState = persister.ViewState.ToString();
string vsRaw = DESCryptoServiceHelper.Decrypt(viewState);
LosFormatter formatter = new LosFormatter();
//解密
return formatter.Deserialize(vsRaw);
}
return null;
}
Web.Config配置
<system.web>
<compilation debug="true" targetFramework="4.0" />
<pages viewStateEncryptionMode="Always" enableViewStateMac="true"></pages>
</system.web>
配置节点pages,第一个属性控制是否加密,第二个控制是否通过Mac地址加密
结果验证
默认前端的ViewState是可以反序列化的,即便通过第一种方法进行加密后也是可以反序列化的,不过反序列化后的内容,其实是加密的结果,也是可以起到保护作用的;
但是通过Web.Config配置加密后,是无法反序列化的,直接就报错了,看来还是.NET自己的招式比较高级。