Secure Session State Transfer Between
ASP.NET and ASP Classic
By Ian Suttle

Ian Suttle
I was discussing Session State interoperability between ASP Classic and ASP.NET with one of my developers. Obviously Microsoft did not provide us, the developer community, with a means of making our ASP.NET applications backwards compatible with ASP Classic. I had come up with a couple of ways to achieve this goal. In running a couple of searches on the subject to see how others had conquered the beast, I came across an article by Peter Bromberg entitled "Transfer Session Variables from Classic ASP to ASP.NET" that described in detail exactly how one of my ideas would work.

In reading his article, like Peter, I was amused to see that so many people had declared this task "impossible!" I am writing this example to give you another possibility for the impossible. This article differs from Peter's in that I will explain how to do the Session State transfer without involving the client, offering an additional level of security (yes, it's important) and automation.

To summarize, the logic would work as follows:

  • ASP.NET function calls an ASP Classic page
  • ASP Classic page dynamically builds an XML structure to represent the ASP Classic Session State contents
  • ASP.NET loads the XML output, and loops through each element, creating ASP.NET Session variables
The Code
The ASP Classic code:

<title>ASPClassicSession.asp</title>
<%
' ASPClassicSession.asp will create the XML structure to be gobbled up by ASP.NET

'set the content type to be of type text/xml
response.ContentType = "text/xml"

'begin writing the xml structure
response.Write "<?xml version=""1.0"" ?>"
response.Write "<session>"

'loop through the session contents, writing each item as if it were an XML element
for each item in session.Contents
     response.Write "<" & item & ">" & session.Contents(item) & "</" & item & ">"
next

'end the xml structure
response.Write "</session>"
%>

 
The preceding page would output a page similar to the following:
<?xml version="1.0" ?>
<session>
     <UserID>87248</UserID>
     <Email>ian@iansuttle.com</Email>
     <UserName>isuttle</UserName>
</session>
 



The ASP.NET (C#) code behind code:

using System.Xml;
using System.Text;
using System.Net;
using System.Net.Sockets;

...

private string RetrieveXML(string server, string page, System.Web.HttpRequest request)
{
     /**********************************************
     * Send the request
     **********************************************/

     //create socket
     IPHostEntry ipHostInfo = Dns.Resolve(server);
     IPAddress ipAddress = ipHostInfo.AddressList[0];
     IPEndPoint ipe = new IPEndPoint(ipAddress, 80);
     Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

     //connect socket to server
     socket.Connect(ipe);

     //Create the request to send to the server
     string strRequest = "GET /" + page + " HTTP/1.1\r\n" +
          "Host: " + server + "\r\n" +
          "Connection: Close\r\n" +
          "Cookie: " + request.Headers["Cookie"] + "\r\n" +
          "User-Agent: " + request.Headers["User-Agent"] + "\r\n\r\n";

     //Convert send data to bytes
     Byte[] bytesSend = Encoding.ASCII.GetBytes(strRequest);

     //Send the data to the server
     socket.Send(bytesSend, bytesSend.Length, 0);

     /**********************************************
     * Receive the return data
     **********************************************/

     //Declare variables for data receipt
     byte[] bytes = new byte[256];
     int nBytes = 0;
     string receive = "";
     string xml = "";

     // The following will block until the page is transmitted.
     do
     {
          nBytes = socket.Receive(bytes, bytes.Length, 0);
          receive += Encoding.ASCII.GetString(bytes, 0, nBytes);
     }
     while (nBytes > 0);

     //We have the page data, but it includes the headers
     // Retrieve XML data from page response
     xml = receive.Substring(receive.IndexOf("<?xml"), receive.Length - receive.IndexOf("<?xml"));

     //Cleanup the socket
     socket.Shutdown(SocketShutdown.Both);
     socket.Close();

     //Return the data
     return xml;
}

public void TransferSession(System.Web.HttpRequest request, System.Web.SessionState.HttpSessionState session)
{
     //Clear the session contents to have a clean session - Optional
     session.RemoveAll();

     //Define the URL and page to load the Session XML from
     string XMLServer = request.ServerVariables["SERVER_NAME"];
     string XMLPage = "aspclassicsession.asp";

     //Define an XMLDocument to allow easy XML tree navigation
     XmlDocument doc = new XmlDocument();

     //Load the document from the reader
     doc.LoadXml(RetrieveXML(XMLServer, XMLPage, request));

     //Loop through the Session element's child nodes and set
     //each Session object
     foreach(XmlNode node in doc.FirstChild.NextSibling.ChildNodes)
     {
          session[node.Name.ToString()] = node.InnerText.ToString();
     }
}

 
On execution of the Page_Load function, the TransferSession function gets called, which consumes the XML data from the ASPClassicSession.asp page, creates the Session contents in ASP.NET, and redirects the user to the specified destination page.

Gathering the XML data is not as easy as it sounds. Since the call from the client browser is going to the server, the server is then making the request for the ASP Classic page to generate the XML. The ASP.NET application does not share your ASP Classic Session State. The relationship between your browser and IIS is dependent on specific headers being passed back and forth. In this example we have ASP.NET gather the headers from the client request, and create our own socket connection to the server using our customized headers to create the illusion that we are the client browser making the request. ASP Classic accepts the request and gladly returns the desired results.

With all applications, you must know your data. The current implementation of this solution will not handle objects or arrays. It would not be difficult to customize the solution to work with specific objects if you know how to detect and rebuild them.

 
Security
This is a subject often skipped due to lack of education or lack of time. Security is out of the scope of this article, but I will present some ideas. This solution has both inherent security and the ability to apply additional security with minimal effort.

Inherent security exists in that execution occurs on the server-side. A user cannot create their own form or edit post variables to choose a different User ID or security level to transfer to.

What if you don�t want your users to have the ability to request the XML feed and see their session variables? Simply identify servers, such as the ASP.NET web servers, that are allowed to request the file and authorize them on your page. A visual should shed some light on this. I will modify the ASP Classic code from above:

The ASP Classic code:

<title>ASPClassicSession.asp</title>
<%
' ASPClassicSession.asp will create the XML structure to be gobbled up by ASP.NET

'Modify the IP address to reflect your allowed server IP(s)
If request.servervariables("REMOTE_ADDR") = "127.0.0.1" then
     'Authorized

     'set the content type to be of type text/xml
     response.ContentType = "text/xml"

     'begin writing the xml structure
     response.Write "<?xml version=""1.0"" ?>"
     response.Write "<session>"

     'loop through the session contents, writing each item as if it were an XML element
     for each item in session.Contents
          response.Write "<" & item & ">" & session.Contents(item) & "</" & item & ">"
     next

     'end the xml structure
     response.Write "</session>"
else
     'Not Authorized

     response.write "You are not authorized to view this page"
     response.end
end if
%>

 
To further secure the data, put it through an encryption / decryption process.
 
Summary
In this article you have seen how to create a very powerful and secure workaround to transfer session information between ASP Classic and ASP.NET. The solution can be manipulated a million different ways to conform to your needs. The most important thing to remember is that what we just did is impossible :).
Ian Suttle is a Microsoft Certified Professional in Visual Basic 6.0. He has 6 years of development experience with web, desktop, and database applications with Microsoft and Java technologies. Ian is currently the lead developer for a national staffing company where he deals with order and vendor management in addition to various other staffing-related applications. He also has a great deal of experience architecting and implementing e-commerce solutions, fraud detection and management, and multi-user interactive environments. You can read more about Ian Suttle at http://www.iansuttle.com
Article Discussion: