How to enforce SSL protection for channels and posting using an HttpModule

Today a question in the newsgroup reminded me that I never created an HttpModule on my own.
I have heard about several samples for HttpModules – mainly to allow custom authentication or to provide an enhanced workflow engine but I have never written one.

So I decided that it is time for me to fill this “black hole” of missing knowledge.

A question came in my mind which came up multiple times in the newsgroup: how to enforce SSL protection for a channel?
There are several different approachs that have been given – even from myself:
1) create a virtual directory in IIS with the same name as the channel and set the “Require SSL” flag
2) modify the template code to include a check if the channel needs to be protected and do a Response.Redirect if required.

None of these two is really perfect. The first has the caveat that it does not cover the unpublished URLs and that navigation controls need to know which items to render with https links and which with http. The seconds requires additional coding in every template.

So I decided to solve this problem using a custom http module.

This has the benefit that I can exactly decide which channels should be protected and which not, without any further modification of the template code.

My code only requires a “RequireSSL” custom channel property to be set to “yes” for the channels which should be SSL protected. Anything else is done by the module – including switching back to non-ssl for other channels.

But one caveat has also this solution: Switching between SSL and http is being done using a Response.Redirect. As the protocol needs to be changed the Location header needs to contain the complete URL including the host name.
This hostname is extracted from the incoming http request. In a web publishing scenario this request might have been redirected by a proxy server to a different name. As my HttpModule now sends the name back to the client the web pubishing server needs to be able to rewrite the location header to the correct name. Microsoft ISA server and other 3rd party servers are able to do this but not all.

But in typical publishing scenarios is the firewall the SSL endpoint and not the CMS server – so the problem would not come up here.

Enough words. Here is the ready-to-use solution:

using System;
using
System.Web;
using
Microsoft.ContentManagement.Publishing;

namespace StefanG.HttpModules
{
    public class
CmsSslHttpModule : IHttpModule 
   
        
       
public void Init(HttpApplication httpApp) 
        {
           
httpApp.PreRequestHandlerExecute += new EventHandler(this
.OnPreRequestHandlerExecute);
        }

        public void Dispose() 
        {
           
// nothing to do…
       
}

        public void OnPreRequestHandlerExecute(object o, EventArgs e)
        {
            HttpApplication httpApp = (HttpApplication) o; 
            HttpContext ctx = HttpContext.Current;

            if (CmsHttpContext.Current != null)
            {
               
if (CmsHttpContext.Current.Channel != null
)
                {
                   
bool RequireSSL = false
;
                   
if (CmsHttpContext.Current.Channel.CustomProperties[“RequireSSL”] != null
)
                    {
                        RequireSSL = (CmsHttpContext.Current.Channel.CustomProperties[“RequireSSL”].Value).ToLower() == “yes”;
                    }

                    if (RequireSSL & !ctx.Request.IsSecureConnection) 
                        ctx.Response.Redirect(“https://”+ctx.Request.Url.Host+ctx.Request.Url.PathAndQuery);
                   
if
(!RequireSSL & ctx.Request.IsSecureConnection)
                        ctx.Response.Redirect(“http://”+ctx.Request.Url.Host+ctx.Request.Url.PathAndQuery);
                }
            }
        }
    }
}

To create this HttpModule create a new C# class library project and replace the code in the class1.css file with the code above. Add a System.Web reference and the necessary CMS references to the project and build the solution.

To install the http module copy the resulting DLL into the bin directory of your template project and add the following entry to the httpModules section of your web.config:

 <add type=”StefanG.HttpModules.CmsSslHttpModule, dllname” name=”CmsSslHttpModule” /> 

dllname needs to be replaced with the name of your dll without the “.dll” part. If your dll is e.g. named CmsSslHttpModule.dll the line would read as follows:

 <add type=”StefanG.HttpModules.CmsSslHttpModule, CmsSslHttpModule” name=”CmsSslHttpModule” /> 

The final step to activate the new http module is to add the RequireSSL custom property to the appropriate channels. Whenever a posting or a channel rendering script of the current project is included in these channel the module will automatically ensure that the request is being done using SSL.

Oh! Btw: the redirect will create these ugly URL’s we love when postbacks and CMS come together. To address these please check out the solution in my other blog which covers this problem (you need to implement Solution for problem 2).

4 Comments


  1. We also chose to go with a custom property on the channel that needed protection. Only thing different was we use a base page for all of our templates and we put the code to handle the redirect there.

    Reply

  2. Hi Rob,

    this is also a possibility.

    But it would require to ensure that all templates and channel rendering scripts are based on this new base class.

    My solution is transparent and works without any modification of the template files.

    Just install the module and you are done.

    Cheers,

    Stefan.

    Reply

  3. Ever thought about protecting parts of your MCMS site with SSL?In IIS you can enforce this for virtual…

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.