Cookieless Sessions and Security

In a previous blog, I pointed out that Microsoft had created an HttpModule that mitigated the ASP.NET cannonicalization issue that was first described a couple of weeks ago. In one of the comments, Amir asked about the security issues surrounding the use of cookieless sessions.  Specifically, he was wondering if ASP.NET could tell if a request containing a cookieless session component was coming from a different browser instance or even a different location.  In brief, the answer is “No“.

When used as part of a plain text web site (i.e. no SSL), cookieless sessions are not secure at all. The session id is placed into the URL in every local link on a page.  The form of the URLs would not be as follows:  http://domain.com/(sessionid)/Page.aspx. This session id is not automatically tied to a specific browser instance or even the IP address of the initial request.  There is no way that I'm aware of to tie the request to a browser (other then using cookies ;). And while a session can be associated to an IP address, this requires some additional work on your part, in the form of an HTTP Module.  The association between session Id and IP address could be done by generating a hash of the IP address for the request (Request.ServerVariables ["REMOTE_ADDR"]) and the session id and using the resulting value to access the session variables.  However, this solution doesn't take into consideration the case where people are behind a proxy that causes the IP address to change from request to request. For this, you might want to just use the first two components of the IP address, since these are not normally varied by the proxy. But if the spoofer is on the same subnet...  As you can see, all in all, this is a difficult problem to solve.

I should probably mention that cookie-based session suffer from the same problem.  There is nothing inherent in how cookied sessions work that makes them safer than their cookieless counterparts.  A hacker can easily create a request that includes a spoofed cookies containing a hijacked session identifier.  But at least a cookie-based session solution can use SSL to encrypt the entire request.  Since the session is not included in the URL directly, it is not easily accessible if the request/response gets hijacked.

So to summarize, while cookieless sessions are nice in theory, in practice they really should only be used on sites where every page is accessed through SSL.  And the implementation should be customized to ensure that a portion of the IP address is used to verify the originating IP address of subsequent requests corresponds to the original request.

Poor operator overloading (or why I'm an evil developer)

In the project that I'm working on, I had the need to overload the Equals operator for a custom class that was being used.   This is a fairly well known problem, although if you haven't done it before, there are enough traps to make it challenging.  More accurately, it is tricky enough that it is better to borrow already working and optimized code then to create it yourself. So naturally, I turn to Google.

I find a couple of MSDN sites, including this one which describes how to do it.  Down towards the bottom of the article, there is a C# example as follows

public static bool operator ==(Complex x, Complex y)
{
   return x.re == y.re && x.im == y.im;
}

Perfect.  I cut, paste, modify to fit my class, run a couple of basic tests and check the code in. 

A few hours later...boom.

All of a sudden some of the other unit tests are failing.  Specifically, statements like the following are throwing a NullReferenceException

if (testValue == null) 

Hold on a second. This is a test for null.  How can it throw an exception.

Here's the catch.  The example I took from MSDN assumes that the objects being compared are structs.  As such, they can never be null, which is why x.re == y.re can never throw a NullReferenceException.  However, if the objects being compared are reference types, then either side can be null.  Which means that the sample code would throw an exception.  The correct overload (taken from here, which also includes a wonderful description of how to optimize Equals) is as follows.

bool operator ==(SomeType o1, SomeType o2)
{
return Object.Equals(o1, o2);
}

This format not only reuses the logic included in the Equals method, it also has one additional benefit.  No NullReferenceExceptions.  Unless, of course, your Equals method throws one. But who would make that mistake. ;)

So why am I evil? Well, in the brief period of time that the bad code was checked in, a colleague updated his sandbox, said update including this bug. Three days later, he runs into a NullReferenceException while checking for null. Wastes time scratching his head wondering why a check for null is throwing a null exception. Suggests that the runtime's parents were closely related. Finally, upon consultation with me (because, honestly, who *expects* that the equality operator will be overloaded), realizes that my old code is the source of the problem. Usually my mistakes impact only me. This one, unfortunately, spread its love just a little bit further.

Missing random files while serializing nested classes

One more post aimed at solving the mystery of a strange error message.  The situation is as follows:  A public class is defined so as to include a nested class.  Within the public class, a method is defined that XML serializes the nested client, the reason being to convert the nested object into a string that can be passed across a service boundary.  The code for the class ressembles the following:

public class OuterClass
{
   class InnerClass
   {
      private string innerProperty;
      public string InnerProperty
      {
         get { return outerProperty; }
         set { outerProperty = value; }
      }
   }

   public void SerializeThis()
   {
     
try
      {
         XmlSerializer s = new XmlSerializer(typeof(OuterClass.InnerClass));
         Console.WriteLine("Serializer created");
      }
      catch (Exception ex)
      {
         Console.WriteLine("Serializer not created: " + ex.Message);
      }
     
finally
      {
         Console.ReadLine();
      }
   }
}

When the SerializeThis method is executed, an exception is thrown when the XmlSerializer is instantiated, specifically a FileNotFoundException with a message of "File or assembly name hzi9lzkp.dll, or one of its dependencies, was not found."  Not an exceptionally helpful message, when it comes to figuring out what's going wrong. 

As it turns out, the problem is that the InnerClass class is not exposed as a public class.  Once that is done, the exception disappears and the serialization works as expected.

IIS 6.0 Isolation Mode and ASP.NET worker process identity

A client ran into an intriguing problem the other day.  The application under development has a number of web services the get deployed onto one server or another as different versions are released for client testing.  Underneath the services, LDAP is used to store roles, preferences and other flotsam and jetsom as needed.  In order to gain access to this information, the IIS_WPG group is given read access to LDAP. Relatively straightforward.

The problem arose when the current version was deployed onto a new machine.  Instead of being able to connect to LDAP, an exception was being thrown.  The ASPNET worker process didn't have access to LDAP.  We were able to connect to the LDAP server, but attempts to negotiate access were being denied.

Four heads are now being scratched.  We removed and add the IIS_WPG user to LDAP.  We do a couple of IISResets to make sure the security context isn't being cached.  Checked the SID that is included as the DN for the IIS_WPG group to make sure something hasn't been installed correctly.  Nothing.  We then gave Everyone access to LDAP.  The service started working again.  So we knew it had something to do with permissions.

In a fit of, well, despiration, we gave permission for the ASPNET user to access LDAP.  Wouldn't you know it.  Things worked again.  But this was unexpected.  One of the things that changed with IIS 6.0 was that ASPNET was no longer the identity under which the worker process runs.  Wasn't it?

As it turns out, the real answer is "it depends".  If you install IIS 6.0 freshly, then IIS_WPG is the group to which the permissions you'd like ASP.NET to have should be assigned.  That is to say, that IIS 6.0 runs in Worker Process Isolation Mode. However, if (and this is the 'if' that caught us) you upgrade from IIS 5.0 to 6.0, the ASPNET user is still the security context for the ASP.NET process.  This can be modified by changing the isolation mode in IIS.  Quite easy, if you know how.  The trick, as we found out, was knowing that we even had to.

 

Getting the App Config Path

Continuing my weekend Internet cruising, I also came across a piece of information that I had wondered about, but never found.  How to get the path to the .config file being used by a .NET application.  Apparently, this information is available through the AppDomain. Specifically, the following method retrieves said path:

AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE")

The things that are available on the Internet if you know where to find them.  Or, in my case, just randomly stumble across them.

Modifying a cell in a DataSet

This post also falls under the “I need to remember this” category, because it's not the first time I've run into the situation.  I was running a quick test of something relatively unrelated (the functionality of the RowUpdating event) when I needed to modify a particular element in a DataSet. The line of code I created to do this was

ds.Tables[0].Rows[0].ItemArray[1] = “Test”;

Seems innocuous enough.  And if you're at all familiar with ADO.NET, this would seem to be a reasonable approach.  In fact, Microsoft even says this is the way to do it here (check out the first C# code...the VB.NET code works fine).  Unfortunately, both Microsoft and reason are mistaken. The correct way to update a cell is as follows

ds.Tables[0].Rows[0].Item[1] = “Test”;

or

ds.Tables[0].Rows[0][1] = “Test”;

The reason for fact that setting the element on ItemArray failing has to do with the actual definition of the ItemArray property.  According to the documentation, ItemArray “gets or sets all of the values for a row through an array”.  In other words, ItemArray is intended to be used an array at a time, not an element at a time. In fact, the getter on ItemArray is actually creating a copy of the values in the DataRow (if you need proof, use a tool like Reflector to check out the code).  So when you do the assignment, you're changing the value of a copy of the values in DataRow, not the underlying value itself.  Which, in turn, means that any DataAdapter.Update performed on the DataSet won't see the change.

Just something to keep in mind.

Pair Programming for the Travel Challenged

I first heard of Facetop through Chris Sells' blog here.  My first thought was "genius".  I think it has the potential to make pair programming possible across distances, something that could make my life as a consultant easier.  And I'm always looking for that.  My second thought coincided with Chris', that being "I want it". 

VS2005 Beta Now Available

In case you haven't heard the news Beta 1 for Visual Studio 2005 (Whidbey) is now available.  You can download it from here.

Two things of note.  First of all, there are two versions of the beta available.  The full-fledged version is available for MSDN Subscribers.  As well, there is a new addition to the family of Visual Studio products.  Or, more accurately, a flock of new additions.  There is are a numner of Express versions available as a free, public beta.   These express versions are light-weight editors and compilers focused on the different languages (VB.NET, C#, C++, J#), development environments (Web Dev) and databases (SQL Server Express).  Very nice if you're looking to give .NET a try and don't have an MSDN subscription. 

The only downside to the beta is that there is no provision in the license to deploy beta applications in a production environment.  Word on the street is that a release license won't be available until beta 2.  Bummer.  I wanted to redevelop the ObjectSharp web site in VS 2005 :)

Humor for Techies

A large percentage of the blog reading that I do regularly is about technical topics.  No offense to any of the authors, but as much as a geek I am, the material is frequently dry.  I'm always looking for something juicy to sink my eyes into. So imagine my delight at finding thedailywtf.com.  Both technical (?) and humorous in one fell swoop.  Thanks to Alex Papadimoulis for making my days

Lowering the Barrier to Entry

If you've been wondering how you can afford a copy of Visual Studio .NET 2003 for playing with at home, you're request has been granted.  Simply watch and review five of the VB at the Movies installments found here and you'll get a certificate for a free Standard Edition.  Not a bad deal at all.