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 calling asp page has to know that it calls it non-asp sub-module and has to call the intermediate page instead passing in the url of the real target page.
The user will see a flicker when the intermediate page is shown and auto-submitted.
But by far the biggest problem is that the session variables are send to the client and then back to the server, just because they are in hidden fields doesn't mean they are save. It was always possible to change the data send back to the server and now with Firefox extensions it is very easy to change the values in hidden form fields before they are sent to the server. A user can just change the value of the user ID to someone else's or increase permissions.
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.
' set some session variables
Session("userid") = "10"
Session("username") = "Peter"
Session("permissions") = "31"
Dim mySessionSecret: mySessionSecret = "mysecret"
Dim sessionSecret: sessionSecret = Request.Cookies("SESSIONSECRET")
Response.Write ("" & vbCrLf)
Response.Write("" & vbCrLf)
If mySessionSecret = sessionSecret Then
For Each sessitem in Session.Contents
If IsObject(Session.Contents(sessitem)) Then
' do not do objects
If IsArray(Session.Contents(sessitem)) Then
' or arrays in this version
' write a XML node for each variable
Response.write("" & Session.Contents(sessitem) & "" & vbCrLf )
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)
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;
// 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);
count = readStream.Read(read, 0, 256);
// Release the resources of stream object.
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)
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();