outman

不断在代码中挣扎.....

博客园 首页 新随笔 联系 订阅 管理
  11 Posts :: 0 Stories :: 118 Comments :: 0 Trackbacks
         浏览器的会话使用存储在 SessionID 属性中的唯一标识符进行标识。会话 ID 使 ASP.NET 应用程序能够将特定的浏览器与 Web 服务器上相关的会话数据和信息相关联。会话 ID 的值在浏览器和 Web 服务器间通过 Cookie 进行传输,如果指定了无 Cookie 会话,则通过 URL 进行传输。
         ASP.NET 通过自动在页的 URL 中插入唯一的会话 ID 来保持无 Cookie 会话状态。例如,下面的 URL 已被 ASP.NET 修改,以包含唯一的会话 ID lit3py55t21z5v55vlm25s55:
               http://www.example.com/s(lit3py55t21z5v55vlm25s55)/orderform.aspx
         如果一个包含无 Cookie SessionID 的链接被多个浏览器共享时(可能通过搜索引擎或其他程序),此行为可能导致对会话数据的意外共享。可以通过禁用会话标识符的回收来降低多个客户端共享会话数据的可能性。为此,将 sessionState 配置元素的 regenerateExpiredSessionId 属性设置为 true。这样,在使用已过期的会话 ID 发起无 Cookie 会话请求时,将生成一个新的会话 ID。
                                                                                                                               ——摘自 MSDN

         .NET2.0中我们已可以通过重写SessionIDManager 类来改变SessionID 的生成机制和验证方法来防止会话数据的意外共享(即出现多个浏览器被识别为同一个会话,共用一个Session),可在.NET1.1中却没有相关的类让我们改变SessionID 的生成机制(封装死了),但受一篇文章的启发,我们可以在SessionID 生成之后对它进行处理。文章是老外写的,由于本人阅读能力有限,偶可没时间去看一大版唧唧歪歪的鹰文,直接下了代码来看。还好代码不算多,思路也很清晰,大概了解了他实现重写SessionID的原理,我改了下在无Cookie 会话中完美实现了。

using System;
using System.Web;
using System.Web.SessionState;
using System.Web.Security;
using System.Configuration;
using System.Security.Cryptography;
using System.Runtime.Serialization;
using System.Globalization;
using System.Text;

public class SecureSessionModule : IHttpModule
{
    
private static string _ValidationKey = null;

    
public void Init (HttpApplication app)
    

        
if (_ValidationKey == null)
            _ValidationKey 
= GetValidationKey ();
        app.AcquireRequestState
+=new EventHandler(app_AcquireRequestState);
    }


    
void app_AcquireRequestState (Object sender, EventArgs e)
    
{
        _ValidationKey
=GetValidationKey();//每天生成一个KEY提高安全性

        HttpContext current  
= ((HttpApplication) sender).Context;

        
//将处理后的SessionID存在Session["ASP.NET_SessionID"]中
        string sessionid = GetSession (current, "ASP.NET_SessionID");

        
if (sessionid != null)
        
{
            
if (sessionid.Length <= 24)
                RedirectUrl(current);

            
string id = sessionid.Substring (024);
            
string mac1 = sessionid.Substring (24);

            
string mac2 = GetSessionIDMac (id, current.Request.UserHostAddress, current.Request.UserAgent, _ValidationKey);

            
// 用户客户端信息发生的变化,比对失败
            if (String.CompareOrdinal(mac1, mac2) != 0)
            
{
                RedirectUrl(current);
            }

        }

        
else
        
{
            RedirectUrl(current);
        }

    }


    
private void RedirectUrl(HttpContext current)
    
{
         
//重定向页面以重新生成新的SessionID
         current.Response.Redirect(current.Request.Url.ToString(),true);
    }


    
private string GetValidationKey ()
    
{
        
string key = DateTime.Now.ToShortDateString();

        
return key;
    }


    
private string GetSession (HttpContext current, string name)
    
{
        
object id = FindSession(current.Session,name);
        
if (id == null
        
{
            
// 将用户客户端信息加密存储在Session中以便比对
            id= current.Session.SessionID+GetSessionIDMac (current.Session.SessionID, current.Request.UserHostAddress,
                current.Request.UserAgent, _ValidationKey);
            current.Session[name]  
= id;
        }

        
return id.ToString();
    }


    
private object FindSession (HttpSessionState session, string name)
    
{
        
return session[name];
    }


    
private string GetSessionIDMac (string id, string ip, string agent, string key)
    
{
        StringBuilder builder 
= new StringBuilder (id, 512);        
        builder.Append (ip);
        builder.Append (agent);

        
using (HMACSHA1 hmac = new HMACSHA1 (Encoding.UTF8.GetBytes (key)))
        
{
            
return Convert.ToBase64String (hmac.ComputeHash (
                Encoding.UTF8.GetBytes (builder.ToString ())));
        }

    }


     
public void Dispose () {}
}
相关配置如下:
<configuration>
  
<system.web>
    
<httpModules>
      
<add name="SecureSession" type="SecureSessionModule,SecureSessionModule" />
    
</httpModules>
  
</system.web>
</configuration>

       大家看了代码后就会知道,它实现的原理主要是因为不可能有相同IP电脑客户端同时访问一台服务器,当出现SessionID相同但他们客户端信息不同时就自动将后一个访问的客户端重定向以新建一个会话。 
     
       遗憾的是在WAP上应用时就有问题了,由于客户端信息没IP唯一标识(移动不给手机号信息了),所以如果相同型号的手机访问时就无法区分,不知哪位高人有没更好的解决办法,还望不吝赐教

题外话:工作忙,时间紧,抄得多,写得少,有问题,请留言。欢迎大家多交流沟通~~~
posted on 2007-02-25 11:54 outman 阅读(2587) 评论(12) 编辑 收藏

Feedback

#1楼 2007-02-25 17:33 JesseZhao      
呵呵,不错啊
 回复 引用 查看   

#2楼 2007-02-25 22:24 Derek[未注册用户]
通过代理上的怎么办?内网若干用户对外只显示一个ip怎么办?
 回复 引用   

#3楼[楼主] 2007-02-26 02:00 outman      
@Derek
呵呵,你这个问题我觉得应该不会发生。。。
具体怎么回事要参考下网络方面的知识,可惜我大学没学好,但是从逻辑上来说相同的IP应该不会出现在相同的IP中。

 回复 引用 查看   

#4楼 2007-02-26 08:26 臭石头      
◎outman
这个问题,一定会发生,很久以前我就试过了。
也建议你参考一下网络安全以及信息安全方面的知识,呵呵。

就算你直接使用Cookie来保存SessionID,这个问题,也一样可以发生。这都是因为HTTP是一个无状态协议造成的。

两台机子,处于同一个内网,系统一致,IE一致,User-Agent就会一致,外网IP也会一致,你上面的方法,就会发生Derek所说的问题了。

如果使用Cookie,A用户访问站点,中途被B截取了,B也就可以使用A的会话了。

要较为彻底一点解决这个安全问题,可以使用SSL,在数字签名的帮助下,大大提高安全性 。

 回复 引用 查看   

处于同一个内网的不同电脑的外网IP是一致的.因为哪有那么多外网IP来用.
 回复 引用   

#6楼[楼主] 2007-02-26 13:23 outman      
@臭石头
@ivan[匿名]
谢谢两位给我补了一课,SSL没弄过也不知道行不行,手机上估计不行吧?

 回复 引用 查看   

#7楼[楼主] 2007-02-26 13:37 outman      
刚GG了一下,真长不少见识:
1. SSL(Secure SocketLayer)是netscape公司设计的主要用于web的安全传输协议。这种协议在WEB上获得了广泛的应用

  2. IETF(www.ietf.org)将SSL作了标准化,即RFC2246,并将其称为TLS(TransportLayer Security),从技术上讲,TLS1.0与SSL3.0的差别非常微小

  3 .在WAP的环境下,由于手机及手持设备的处理和存储能力有限,wap论坛(www.wapforum.org)在TLS的基础上做了...S协议(Wireless Transport Layer Security),以适应无线的特殊环境。

可是我当初研究这个东西只是想简单的验证下,至于复杂严密的验证我觉得如果代价不大(累得半死结果老板还以为你在偷懒)的话改成这种最好,但报着学习的态度私下我会抽时间好好研究下 :)

 回复 引用 查看   

#8楼 2007-02-27 08:44 臭石头      
@outman
你当真认为User-Agent包含客户端MAC么?呵呵,我这有六十万访问数据,没有一个是带有MAC的,手机的我没做过,不敢确定。

给你看一个列子:
Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+SV1;+.NET+CLR+2.0.50727)

 回复 引用 查看   

#9楼[楼主] 2007-02-27 11:02 outman      
@臭石头
偶错了~``,那怎么办?不可能真要用SSL吧?感觉用了后挺慢的,这个代价太昂贵。。。

 回复 引用 查看   

#10楼 2007-02-27 15:13 臭石头      
呵呵,我知道不多。
但据我所知,http中,只有用SSL才是最安全的,普通的http通信,简直不堪一击。就算客户端支持Cookie,还是不堪一击的。

我做过这方面的应用,呵呵。

 回复 引用 查看   

#11楼 2008-01-17 17:33 小罗      
哥们,这个验证是不是有些问题,我有些疑问.

string sessionid = GetSession (current, "ASP.NET_SessionID");
string id = sessionid.Substring (0, 24);
string mac1 = sessionid.Substring (24);
string mac2 = GetSessionIDMac (id, current.Request.UserHostAddress, current.Request.UserAgent, _ValidationKey);

// 用户客户端信息发生的变化,比对失败
if (String.CompareOrdinal(mac1, mac2) != 0)

sessionid 得到的值是从current中取的。
mac2 中得到的值也是从current中取的。
这样比较是会不会怎么比较会使表达式String.CompareOrdinal(mac1, mac2)返回值为0?

还有可不可以指导下怎么在程序中用这段代码!



这段我设置报错。帮忙指导下,我也是做Wap这方面,很想和你交流交流。我也在做用户SessionID验证这地方卡住了。期待你的回复。

 回复 引用 查看   

@小罗
程序是配置在webconfig 里面全站公用的...

 回复 引用