Thursday, December 1, 2011

RequireHttpsAttribute breaks my app in Cassini

I was reading blog entry yesterday by Troy Hunt on Transport Layer protection. Beyond being a good reminder of some basic security practices, it reminded me of a little trick I use to get a site to require secure HTTP for production and staging installs, but still be able to debug it locally using Cassini (which doesn’t support HTTPS).  An alternative way to run your app locally would be to use IIS Express with an actual certificate, self-signed or otherwise, but this way is relatively simple and has the added advantage that you know that the attribute is applied globally.
I take advantage of GlobalFilters introduced in MVC 3.  The default template for your Global.asax.cs file has a method called RegisterGlobalFilters.  I’ve cloned this method as:

public static void RegisterProductionOnlyFilters( GlobalFilterCollection filters )
{
    filters.Add( new RequireHttpsAttribute() );
}

This method is then called in Application_Start() as follows:

var requestIsLocal = false;
try
{
    requestIsLocal = this.Context.Request.IsLocal;
}
// if the request isn't available, we catch the exception and know we're on prod/staging
catch (NullReferenceException) { }
 
if (!requestIsLocal)
{
    RegisterProductionOnlyFilters( GlobalFilters.Filters );
}

Now the RequireHttpsAttribute is applied as a global filter, but only when not running on localhost. To complete security, we use Web.config transformations to add requireSSL=”true” to our auth and session cookies (all cookies created server-side, that is).
<system.web>
    <authentication mode="Forms">
        <forms requireSSL="true" xdt:Transform="SetAttributes(requireSSL)" />
    </authentication>
    <httpCookies requireSSL="true" xdt:Transform="InsertAfter(/configuration/system.web/authentication)" />
</system.web>

Voila, our actions and cookies are now protected, but only when running in our production and staging environments.