Recently, I needed to ensure that a whole ASP.NET web application was forced to deliver content over a secure connection. Almost every page in the application contained sensitive data, and is was deemed acceptable for the small number of pages which didn’t contain secure content to be delivered over HTTPS too. I started looking around for possible solutions and found several.
Deliver the whole web application as HTTPS by playing around with SSL Settings in IIS Manager. If a user requests a page using HTTP, they’ll be presented with a 403.4 error. While this is easy to implement, the error presented to the user is not an acceptable solution to me.
Modifying the custom error 403.4 to point to a script which redirects to https and set the site to require SSL. I couldn’t get this to work personally, maybe IIS 7 behaves differently? Besides that, I wasn’t wildly keen on using classic ASP either, which is the approach taken here.
Writing a custom HTTP module to detect a request to HTTP and redirect to HTTPS. This seems a pretty neat idea, but writing a custom module to do this seemed a little like overkill.
Setting require SSL and then configuring IIS to respond to a 403 error by responding with a 302 redirect to the root of the app using https. This means that a user is redirected to the root of the site, rather than the page they initially requested, which could cause confusion.
None of these solutions felt particularly right to me, so I looked at a custom URL rewriting module which could do this, and much more for me.
I used the open source project UrlRewritingNet to write a custom rule to pass HTTP requests to HTTPS. It’s a powerful URL rewriter which can carry out redirects and rewrites, use regular expressions to match incoming URLs and much more. The setup involves no code, other than making changes to Web.config and dropping the assembly into the bin folder of the web application.
To demonstrate the method, I’ve started a website from scratch in order that you can see how simple this is. To start, I created a new virtual directory in IIS called “SecurityTest”, pointing to an empty folder, called “SecurityTest”.
Inside SecurityTest, I created a “bin” folder, and copied in the DLL file from UrlRewritingNet.
Next, I created a Web.config file in the “SecurityTest” folder, and gave it the minimal configuration to get this working:
<?xml version="1.0"?> <configuration> <configSections> <section name="urlrewritingnet" restartOnExternalChanges="true" requirePermission="false" type="UrlRewritingNet.Configuration.UrlRewriteSection, UrlRewritingNet.UrlRewriter"/> </configSections> <system.webServer> <modules> <remove name="UrlRewriteModule" /> <add name="UrlRewriteModule" type="UrlRewritingNet.Web.UrlRewriteModule, UrlRewritingNet.UrlRewriter"/> </modules> </system.webServer> <urlrewritingnet rewriteOnlyVirtualUrls="false" contextItemsPrefix="QueryString" xmlns="http://www.urlrewriting.net/schemas/config/2006/07"> <rewrites> <add name="secure" virtualUrl="http\://(.*)" destinationUrl="https://$1" ignoreCase="true" redirect="Domain" /> </rewrites> </urlrewritingnet> </configuration>
You can see that there are three sections. Of course, these can be included in a fully featured Web.config easily too.
Firstly, we declare the section urlrewritingnet by adding a <section> element inside <configSections>.
Next, we add a module inside <system.webServer> so that all requests are routed through the rewriter module, allowing it to do its stuff.
Finally, we add the urlrewritingnet section itself, and add a rule which maps all http:// requests into https://. This is done by mapping source expressions which match this regular expression:
into this destination:
where $1 means insert the text matched in the first selector (.*) in the source regular expression. We tell the rule to cause a redirect by adding the
redirect="Domain". This is important, otherwise the request would only be rewritten as https on the server side, and still transmitted as http over the network.
Finally, I created the simplest possible Default.aspx page like this:
<html> <head> <title>Security Test</title> </head> <body> <h1>Security Test</h1> <p>Attempting to access this page using http should automatically redirect to https.</p> </body> </html>
Now, in my browser, I navigated to http://localhost/SecurityTest/Default.aspx. The rewrite rule picked matched the rule, with $1 being set as localhost/SecurityTest/Default.aspx, and the request being redirected to https://localhost/SecurityTest/Default.aspx as expected!
An Expected but Non-Existent Drawback
I was expecting that the problem with any approach using HTTP Modules are that it would only work if ASP.NET is going to be responsible for displaying the content, and that .html files would not be handled by the ASP.NET pipeline. I changed the file extension of Default.aspx to .html and browsed to the page. I didn’t expect the redirect to work, as I thought that ASP.NET wouldn’t be used to serve the file. However, the redirect did work, which puzzled me.
Perhaps someone could explain how this worked to me?