A better way to share ASP Sessions

24 September 2007
I'm currently doing some consulting work in a company that is still exclusively using classic ASP as their web technology. There is no way for them to move to ASP.Net by starting over or migrating their whole application framework in one go. The idea now is to allow to write sub-modules in asp.net which are then integrated into the existing classic asp infrastructure. One problem here is the sharing of session variables which hold information about the logged on user including some permissions.

They did some research and found a solution that several people online recommend: Instead of calling an aspx page in a sub-modules directly, the source asp page calls a intermediate page which puts all existing asp session variables into hidden form fields and then auto-submits the form to an aspx page. That page then picks up the hidden values and puts it into an asp.net session. From now on the sub-module has a read-only copy of the asp session.

There are several problems with this approach: The user can control the content of the session copied to the asp.net module. Session data should never leave the server.

Which brings me to the solution I recommend:
The calling asp page calls any aspx page without having to pass anything in or going through an intermediate page. In Page_Load each aspx page calls a method in a common class to check for an existing session. This class, lets call it UserState wraps all access to the asp.net session object. If there is no session object it knows it has to get it from the parent asp application. To do this it calls a special page 'sessionstate.asp' which returns XML with all the session variables of the current user. The data in the session can then be made available as a hashtable, or even strongly typed properties if the various session variables are known during design time.
The UserState class uses an webRequest to call the sessionstate.asp page which is on the same server and the data transfer never leaves the server. To get the correct session variables UserState takes the ASP Session cookie and passes it along with the request to the sessionstate.asp page.
To make the call to sessionstate.asp more secure I use a simple secret string which both 'sessionstate.asp' and the UserState class share. This is also passed in as a cookie to 'sessionstate.asp' and only if it matches it's own value it returns data. One could also limit access to 'sessionstate.asp' to callers from the same IP address by using IIS access security. Or encrypt the session XML synchronously and have the two sides share the same key.

Sample code:

source.asp:
<%
  ' set some session variables
  Session("userid") = "10"
  Session("username") = "Peter"
  Session("permissions") = "31"  
%>
Sessionstate.asp:
    Dim mySessionSecret: mySessionSecret = "mysecret"   
    Dim sessionSecret: sessionSecret = Request.Cookies("SESSIONSECRET")
       
    Response.Write ("" & vbCrLf)
    Response.Write("" & vbCrLf)
    
    If mySessionSecret = sessionSecret Then
   
    	Dim sessitem
   
	For Each sessitem in Session.Contents
	  If IsObject(Session.Contents(sessitem)) Then
          ' do not do objects
	  Else
	  	If IsArray(Session.Contents(sessitem)) Then
	          ' or arrays in this version
	        Else
                  ' write a XML node for each variable
		   Response.write("" & Session.Contents(sessitem) & "" & vbCrLf )
		End If
	   End If
	Next 
		
    End If
		
    Response.Write("")   
target.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
using System.Text;

public partial class _Default : System.Web.UI.Page
{
    string _sessionSecret = "mysecret";
    string _classicASPSessionPage = "http://localhost/sessionstate.asp";

    protected void Page_Load(object sender, EventArgs e)
    {
       CheckSession();
    }

    private void CheckSession()
    {
        Uri hosturi = new Uri(_classicASPSessionPage);

        HttpWebRequest myWebRequest = (HttpWebRequest)WebRequest.Create(hosturi);
        myWebRequest.Method = "GET";
        myWebRequest.Timeout = 3000; // three seconds
        AddCookie(ref myWebRequest, hosturi);
        
        lblValues.Text = Server.HtmlEncode(GetResponseBody(myWebRequest));
    }

    private string GetResponseBody(HttpWebRequest myWebRequest)
    {
        WebResponse myWebResponse = myWebRequest.GetResponse();

        Stream ReceiveStream = myWebResponse.GetResponseStream();

        Encoding encode = System.Text.Encoding.GetEncoding("utf-8");

        // Pipe the stream to a higher level stream reader with the required encoding format. 
        StreamReader readStream = new StreamReader(ReceiveStream, encode);
        Char[] read = new Char[256];

        // Read 256 charcters at a time.    
        int count = readStream.Read(read, 0, 256);

        StringBuilder body = new StringBuilder();

        while (count > 0)
        {
            // Dump the 256 characters on a string and display the string onto the console.
            String str = new String(read, 0, count);
            body.Append(str);
            count = readStream.Read(read, 0, 256);
        }
        // Release the resources of stream object.
        readStream.Close();
        myWebResponse.Close();

        return body.ToString();
    }

    private void AddCookie(ref HttpWebRequest request, Uri url)
    {

        string aspSessionKey = string.Empty;
        string aspSessionValue = string.Empty;

        // find the classic asp session cookie, we need it
        foreach (string key in Request.Cookies.AllKeys)
        {
            if (key.StartsWith("ASPSESSION"))
            {
                aspSessionKey = key;
                aspSessionValue = Request.Cookies[key].Value.ToString();
            }
        }

        // add the asp session cookie
        Cookie cookie = new Cookie(aspSessionKey, aspSessionValue);
        cookie.Expires = DateTime.Now.AddHours(24);
        cookie.Domain = url.Host;
        cookie.Path = "/";

        // add a secret that only I and the target page know
        Cookie secretCookie = new Cookie("SESSIONSECRET", _sessionSecret);
        secretCookie.Expires = DateTime.Now.AddSeconds(10);
        secretCookie.Domain = url.Host;
        secretCookie.Path = "/";

        request.CookieContainer = new CookieContainer();
        request.CookieContainer.Add(cookie);
        request.CookieContainer.Add(secretCookie);
    }

}

Pages in this section

Categories

ASP.Net | Community | Development | IIS | IT Pro | Security | SQL (Server) | Tools | Web | Work on the road | Windows