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.
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); } }