asp.net 中的截取 Session 变量
//================================
作者信息:Robbe Morris 是2004Microsoft的MVP(Visual C#)和高级软件工程师他是EggHeadCafe.com的一个工程师,EggHeadCafe.com支持网站开发工程师
(注意:本问未征得原作者同意,本人仅做学习之用,如有不便敬请见谅)
原文地址:http://www.eggheadcafe.com/articles/hijacksession.asp
//================================
愚蠢的用户骗局!我这样称呼我工作的地方。那些用户根本无法通过输入那些你
告诉他得到他工作和生活上需要的东西,网站不能完成这样的工作。
众所周知,这是事实。能够看到他们输入的和尽可能的近距离体验到他们
当时的处境看起来不好吗!今天往往是妈妈赋予我们更多灵感显得多么的滑稽!
事情经常发生以至我常常为一个新的请功能,使用模块去截取http请求,为的是
跟踪一个浏览我们经常被质疑网站的用户最后使用的页面(我将在下面简要的说明)
在一个双修日的修补后,我提出了一个原形或事实上是在一个现存网站上一个尽可能少改变的方案。
下面截取http请求的编码技术已经被许多技术指南站点和编程书籍提到。因此,我
不会再用基础知识烦你,有一个来自msdn的连接可以供我们使用:《在ASP.NET.用http管道执行安全的请求处理,过滤和内容定向》。我们的目标是找到一个允许技术支持去摄取或截取用户Session变量和自动的发送技术支持页面到用户使用的最后一页的技术。可能我们不需要去寻找哪个用户和他们的登陆信息或着询问许多关于他们在网站的什么地方,他们怎么到达那了的问题。他们是怎么到那里的。如果你已经听了技术支持的电话你就知道对于用户和技术支持这是多么的困难和另人沮丧。
第二个目标,他将帮助技术支持能够监控表单元素提交的的真实时间。
最后,但不是最小,实现这个方案对现存的网站没有一点的改变。而且使跨越多个站点成为可能。这是为一个产品定单程序预备。这就是本文的目的。我将保持简述的简单性在一个nutshell,他是怎么工作的?
代码例子由一个名为TechSupport的asp.net程序,和一个叫IISCapture程序集组成.
TechSupport只是一个demo而已例子包含了6行代码成两个页面来实现这个方案。
第一个页面的三行代码在askforhelp.asp。它在当前的程序中创建一个新的有问入口,
把当前的Session信息保存在Cache中,Cach保存有问题入口信息和用户访问的最后的页面标记这个用户作为一个技术支持模型,设置一个合理的过期期限给这个Cache中以便不会在内存中消失。当用户有另一个请求页面的时候,技术支持模式将允许我们从一个元素值以及 任何其他我们觉得对于我们的技术支持是有用的东西传输到更新的Session值,
//=========================================
askforhelp.aspx
string Url=Request.QueryString["techsupporturl"].ToString();
IISCapture.TroubleTicket oTicket = new IISCapture.TroubleTicket();
msSessionKey = oTicket.LoadNewSessionToApplication(System.Web.HttpContext.Current,Url);
//==============================================
第二是技术支持自己登陆的页面。在同一个例子中
技术支持需要简单的舔添入有问题的入口编号接着提交。
从这儿,我们将用有问题的入口去发现用户Session数据,从一个元素数据
和其他任何的程序Cache中,重复这些值给他们给技术支持查看。
接着定向他们到用户最后登陆的页面。也许你这个产品程序的解决方案应该包括更多的安全测试以方便技术支持记录错误信息。
//===================================================================
techsupportlogin.aspx
IISCapture.TroubleTicket oTicket = new IISCapture.TroubleTicket();
oTicket.LoadSessionFromApplication(System.Web.HttpContext.Current,this.TextBox1.Text);
oTicket.TransferToUrl(System.Web.HttpContext.Current);
//=====================================================================
现在对于任何的产品网站我们的两个页面都是新的。我们通过在所有的网站增加一个hyperlink管理这个实现方案,所有的逻辑都发生在外部的IISCapture程序集中,它能截取http请求。
TroubleTicket 类的操作数据和Log类处理拦截的http请求。匆匆一瞥之后,你可能想这个方案不能在一个web farm环境中工作。这不完全正确IISCapture.TroubleTicket.LoadSession可以被修改以,便保在执行的Url中保存
服务端的ip地址,以用来取带域名。一旦你使用Session(强迫每个并发页面请求都发送的服务上),技术支持登陆同样的服务(在你的程序中,你可以决定什么时候终止和包含一个登陆连接), 如果不用这个选择,那么用一个数据库保存objects转化为字节数组,好过我在本文中使用Cach保存。在看源代码Log and TroubleTicket类之前
让我们首先讨论截取用户请求
1. 分析用户详Web踪迹
四个月前你建了个网站,您收到一个请求,是关于要求报告用户注意你网站的什么方面,访问页什么页面的。自然的从发展阶段提出这个需求开始到最后期限到来时,他们说它不是必须的,因为你没有能跟踪自己的踪迹。其实你可以简单的增加一个http module去截取页面请求,把他写入一个集中数据库。如果你已经在Session变量中包含了一个用户id或着把它隐藏在表单中你就可以提取这些请求写入日志中;如果没有,就在你的登陆存储过程中增加一些代码把USerID和SessionID保存数据库中的一个表里。你可以把登陆页面的SessionID加入到你的UserID中。总之尽量的减少改
变,对于你的网站是必须的,同时我们也已经实现的你的要求。
2。自由的改变输出流
我的代码在FilterHtml中,你可以查看。用FilterHtml覆盖Response.Filter类在Log类中ReleaseRequestState事件中有详细注释FilterHtml class类用中Filter.Html.Write 方法覆盖 Response.Filter.Write,并允许你修改输出到浏览器中的流。注释说明了为什么这样做
可能有许多原因,你想改进流。一个让我注意到是安全理由。作为防止黑客入侵,你可以在输出流中写一个查询,看看有没有关于你网站,数据库服务器的危险信息,或其他不同的形式黑客可能采取的进攻痕迹比如SQL注入式攻击,恶意格式请求等等。
当前网站容易受各种类型的攻击,这个应该成为你清理代码阻断这些缺口的一个重要理由 FilterHtml 类 可以被调整以便传递一个ref 的字符串.返回html stream ,被程序发送到浏览器的通过修改FilterHtml 类,传递一个refref 的字符串,可以返回程序发送到浏览器的Html流, 监控什么被发送到客户端。
3. 传递给Session值给别的站点
你可以用server.transfers欺骗别的你操作的站点。当进入或离开这个网站 一个在站点A,B都存在的http截取模块可以同时起作用,如果发现一个变化,在一个集中的地方比如数据库捕获和保存这个Session数据创建一个唯一的入口并把他增加到url中,执行、server.transfer传送到目标页面当站点B等到这个请求,他就调用用Session值发出一个response.redirect定向到一个目标页面并在Url中减掉入口标签。当你统揽这个例子时,确定已经读过AskForHelp.aspx的说明本质上,你可以从ebform1.aspx开始点button to, 注册你喜欢的名字button发送你注册信息的到webform2.aspx,那里你可以点技术支持连接打一个新窗口打开,它指向一个有问题的用户入口 。拷贝/粘贴 url到新窗口,浏览器将显示作为技术支持登陆看到的页面在方法体中设置不同的短点,你可以看到程序是运行的。
让我们来看看Log and TroubleTicket类,他们是怎么工作的
Log.cs
using System;
using System.Web;
using System.Web.SessionState;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Text;
namespace IISCapture
{
public class Log : IHttpModule
{
HttpApplication HttpApp;
HttpRequest HttpReq;
HttpResponse HttpRes;
public void Init(HttpApplication App)
{
// First event in the event chain
App.BeginRequest += new EventHandler(OnBeginRequest);
// Session state for the request isn't available until this event fires.
App.AcquireRequestState += new EventHandler(AcquireRequestState);
// If you want to alter the output stream on the fly, do it here.
App.ReleaseRequestState += new EventHandler(ReleaseRequestState);
}
// Application Context Events
private void OnBeginRequest(object sender, EventArgs eventArgs )
{
HttpApp = (HttpApplication)sender;
HttpReq = HttpApp.Context.Request;
HttpRes = HttpApp.Context.Response;
try
{
}
catch (Exception err) { ProcessError(err.Message); }
}
private void AcquireRequestState(object sender, EventArgs eventArgs )
{
string Ticket="";
string Url="";
try
{
// Reload application cache with the user's session info and
// also grab the last posted form elements and their values.
if (this.IsSessionInTechSupportMode()==true)
{
IISCapture.TroubleTicket oTicket = new IISCapture.TroubleTicket();
Ticket = HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportKey].ToString();
Url = HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportUserLastPage].ToString();
oTicket.LoadSessionToApplication(HttpApp.Context,Ticket,Url);
oTicket.LoadFormToApplication(HttpApp.Context,Ticket);
return;
}
// get the problem user's session info again.
if (this.IsSessionATechSupportRep()==true)
{
IISCapture.TroubleTicket oTicket = new IISCapture.TroubleTicket();
Ticket = HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportKey].ToString();
oTicket.LoadSessionFromApplication(HttpApp.Context,Ticket);
// Here is a sample of how to read the posted form values from the user
// needing technical support:
try
{
object[,] oValues = oTicket.GetFormFromApplication(HttpApp.Context,Ticket);
for(int i=0;i<=oValues.GetUpperBound(1);i++)
{
Debug.Write(oValues[0,i].ToString() + ": ");
Debug.WriteLine(oValues[1,i].ToString());
}
}
catch { }
}
}
catch (Exception err) { ProcessError(err.Message); }
}
private void ReleaseRequestState(object sender, EventArgs eventArgs )
{
// This event is a good choice for apply filters to the output
// stream to the browser/client. By overriding the filter, you
// can modify the output stream after all the response.write's are
// done and before it gets to the browser.
// Just add your own business rules to determine whether a filter
// should be applied at all.
try
{
if(HttpRes.ContentType != "text/html") { return; }
// HttpRes.Filter = new IISCapture.FilterHtml(ref CaptureSession,HttpRes.Filter);
}
catch (Exception err) { ProcessError(err.Message); }
}
// Custom methods
private bool IsSessionInTechSupportMode()
{
bool Ret = false;
try
{
if (HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportEnable] != null)
{
if (HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportEnable].ToString() =="1")
{
Ret=true;
}
}
}
catch { }
return Ret;
}
private bool IsSessionATechSupportRep()
{
bool Ret = false;
try
{
if (HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportRep] != null)
{
if (HttpApp.Context.Session[IISCapture.TroubleTicket.TechSupportRep].ToString() =="1")
{
Ret=true;
}
}
}
catch { }
return Ret;
}
private void ProcessError(string ErrMsg)
{
HttpApp.Context.Response.Write(ErrMsg);
}
public void Dispose(){}
}
}
TroubleTicket.cs
using System;
using System.Web;
using System.Collections;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace IISCapture
{
public class TroubleTicket
{
public const string TechSupportUserLastPage = "TechSupportUserLastPage";
public const string TechSupportEnable = "TechSupportEnable";
public const string TechSupportKey = "TechSupportKey";
public const string TechSupportRep = "TechSupportRep";
public const string TechSupportCache = "TechSupportCache_";
public const string TechSupportCacheSession = "Session_";
public const string TechSupportCacheForm = "Form_";
public TroubleTicket()
{
}
public void TransferToUrl(HttpContext oContext)
{
try
{
oContext.Response.Redirect(oContext.Session[TechSupportUserLastPage].ToString());
}
catch (Exception) { throw; }
}
public string LoadNewSessionToApplication(HttpContext oContext,string GotoUrl)
{
string TroubleTicket="";
try
{
TroubleTicket = System.Guid.NewGuid().ToString();
LoadSessionToApplication(oContext,TroubleTicket,GotoUrl);
LoadFormToApplication(oContext,TroubleTicket);
}
catch (Exception) { throw; }
return TroubleTicket;
}
public void LoadSessionToApplication(HttpContext oContext,string TroubleTicket,string GotoUrl)
{
try
{
oContext.Session[TechSupportUserLastPage] = GotoUrl;
oContext.Session[TechSupportKey] = TroubleTicket;
object[,] oValues = new object[2,oContext.Session.Keys.Count];
for(int i=0;i<oContext.Session.Keys.Count;i++)
{
oValues[0,i] = oContext.Session.Keys[i].ToString();
oValues[1,i] = oContext.Session[oContext.Session.Keys[i].ToString()];
}
oContext.Session[TechSupportEnable] = "1";
if ((object[,])oContext.Cache[TechSupportCache + TechSupportCacheSession + TroubleTicket.Trim()] != null)
{
oContext.Cache.Remove(TechSupportCache + TechSupportCacheSession + TroubleTicket.Trim());
}
oContext.Cache.Insert(TechSupportCache + TechSupportCacheSession + TroubleTicket, _
oValues,null,DateTime.MaxValue, TimeSpan.FromMinutes(10));
}
catch (Exception) { throw; }
return;
}
public void LoadFormToApplication(HttpContext oContext,string TroubleTicket)
{
bool Found=false;
try
{
object[,] oValues = new object[2,oContext.Request.Form.Keys.Count];
for(int i=0;i<oContext.Request.Form.Keys.Count;i++)
{
oValues[0,i] = oContext.Request.Form.Keys[i].ToString();
oValues[1,i] = oContext.Request[oContext.Request.Form.Keys[i].ToString()];
Found=true;
}
if ((object[,])oContext.Cache[TechSupportCache + TechSupportCacheForm + TroubleTicket.Trim()] != null)
{
if (Found==true)
{
oContext.Cache.Remove(TechSupportCache + TechSupportCacheForm + TroubleTicket.Trim());
}
}
oContext.Cache.Insert(TechSupportCache + TechSupportCacheForm + _
TroubleTicket,oValues,null,DateTime.MaxValue, TimeSpan.FromMinutes(10));
}
catch (Exception) { throw; }
return;
}
public string LoadSessionFromApplication(HttpContext oContext,string TroubleTicket)
{
string GotoUrl="";
try
{
object[,] oValues = (object[,])oContext.Cache[TechSupportCache + _
TechSupportCacheSession + TroubleTicket.Trim()];
for(int i=0;i<=oValues.GetUpperBound(1);i++)
{
oContext.Session[oValues[0,i].ToString()] = oValues[1,i];
}
oContext.Session[TechSupportEnable] = "0";
oContext.Session[TechSupportRep] = "1";
GotoUrl = oContext.Session[TechSupportUserLastPage].ToString();
}
catch (Exception) { throw; }
return GotoUrl;
}
public object[,] GetFormFromApplication(HttpContext oContext,string TroubleTicket)
{
object[,] oValues = null;
try
{
oValues = (object[,])oContext.Cache[TechSupportCache + TechSupportCacheForm + TroubleTicket.Trim()];
}
catch (Exception) { throw; }
return oValues;
}
}
}