Introduction
When we, as developers, encounter the same coding scenario time and time again, we naturally tend to encapsulate the coding logic and reuse it in an effort to save time and minimize maintenance.
Recently, while developing a website called Bigshot Hotshot, I reevaluated the need to switch between secure (HTTPS/SSL) and non-secure (HTTP/non-SSL) pages. I noticed that while coding, we do not, in most cases, think about using SSL. One reason is that (at the time of this writing) the ASP.NET Development Server does not support SSL. To test SSL pages, we need to add our application/website to IIS and configure it accordingly. Another problem I wanted to solve was how to hint to IIS that certain pages should always use HTTPS while others should always use HTTP. To complicate things even further, I wanted to take SEO (search engine optimization) into account as well so that redirecting between secure and non-secure pages does not have a negative impact on the website's SEO.
This article presents one way of solving the aforementioned issues. For brevity, we will abbreviate the phrase: switch(ing) between HTTP and HTTPS to HTTP <=> HTTPS.
Background Research
Solution/Proposal 1
While researching potential solutions for the issue of HTTP <=> HTTPS, I came across an article by Matt Sollars: Switching Between HTTP and HTTPS Automatically: Version 2. It is a well-written solution to the above problem. I like how you can enforce entire directories to use SSL, as well as individual pages. I also like the web.config based approach to specify which files should use SSL. On the other hand, the solution is more complicated than what I needed. In addition, HTTP <=> HTTPS by calling Response.Redirect([path], true)
, and ending the Response
is not very SEO friendly.
Solution/Proposal 2
Another solution to HTTP <=> HTTPS I came across was by Yohan B: RequireSSL Attribute for ASP.NET. I like the Attribute
based approach of specifying that certain pages are required to use SSL. I also like the use of the #if DEBUG
directive to tell the compiler not to HTTP <=> HTTPS while running in Debug mode (since ASP.NET Development Server does not support SSL anyway). What I am not quite fond of, however, is the use of a base Page
that all other Page
s inherit from to call the Validate()
method and control HTTP <=> HTTPS. Also, just like in the above article, HTTP <=> HTTPS by calling Response.Redirect([path], true)
, and ending the Response
is not very SEO friendly.
Our Strategy
What we will be looking at in the next section is another way to HTTP <=> HTTPS. We will use Attribute
s to mark which Page
s require SSL, and we will implement a custom HTTP module responsible for intercepting requests to our ASPX pages and for HTTP <=> HTTPS when necessary. We will also examine how to do this in an SEO friendly manner.
The Code
First off, we need to define an Attribute
so we can decorate the Page
s that require SSL with that Attribute
. Let's define an Attribute
called RequireSSL
:
[AttributeUsage(AttributeTargets.Class)]
sealed public class RequireSSL : Attribute
{
}
In our example project, the login.aspx and signup.aspx pages require SSL. We will mark them accordingly (notice the RequireSSL
attribute):
[RequireSSL]
public partial class login : System.Web.UI.Page
{
...
}
[RequireSSL]
public partial class signup : System.Web.UI.Page
{
...
}
Next, we will implement our custom HTTP module. It will be configured so that the code for HTTP <=> HTTPS only runs when compiled in Release mode:
public class RequireSSLModule : IHttpModule
{
public void Init(HttpApplication context)
{
#if !DEBUG
context.PreRequestHandlerExecute +=
new EventHandler(OnPreRequestHandlerExecute);
#endif
}
...
}
Let's take a closer look at the PreRequestHandlerExecute
event.
void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
System.Web.UI.Page Page =
HttpContext.Current.Handler as System.Web.UI.Page;
if (Page == null)
{
return;
}
bool requireSSL = (Page.GetType().GetCustomAttributes(
typeof(RequireSSL), true).Length > 0);
bool isSecureConnection =
HttpContext.Current.ApplicationInstance.Request.IsSecureConnection;
Uri baseUri = HttpContext.Current.ApplicationInstance.Request.Url;
if (requireSSL && !isSecureConnection)
{
string url = baseUri.ToString().Replace(
baseUri.Scheme, Uri.UriSchemeHttps);
PermanentRedirect(url);
}
else if (!requireSSL && isSecureConnection)
{
string url = baseUri.ToString().Replace(baseUri.Scheme,
Uri.UriSchemeHttp);
PermanentRedirect(url);
}
}
I've commented the code above, and it should be fairly straightforward. There are a couple of things I'd like to note:
- The first is that the code has been optimized to only HTTP <=> HTTPS when necessary.
- Second, the only pages allowed to use SSL are those marked with the
RequireSSL
module. These will be redirected to HTTPS, while all others will automatically be redirected to use HTTP. - The third thing I'd like to point out is the method
PermanentRedirect([url])
. This method performs a 301 redirect. This is the most search engine friendly way of redirecting to another Page
. The 301 status code means that a Page
has permanently moved to a new location. It is implemented as:
private void PermanentRedirect(string url)
{
HttpContext.Current.Response.Status = "301 Moved Permanently";
HttpContext.Current.Response.AddHeader("Location", url);
}
In the upcoming ASP.NET 4.0, Microsoft has added PermanentRedirect
to the ASP.NET framework itself. You can read more about it here.
The only thing left to do now is to register the RequireSSL
module inside the web.config file. Note: You can safely register the module in both places below, so that you needn't worry about whether you are running your website on IIS6 or IIS7.
For IIS6 or IIS7 running in Classic Mode
<configuration>
<system.web>
<httpModules>
<add name="RequireSSL" type="Code.RequireSSLModule, Code" />
</httpModules>
</system.web>
</configuration>
For IIS7 running in Integrated Mode:
<configuration>
<system.webServer>
<modules>
<add name="RequireSSL"
preCondition="managedHandler"
type="Code.RequireSSLModule, Code" />
</modules>
</system.webServer>
</configuration>
Running the Code
Running the code from Visual Studio is as easy as pushing F5 on your keyboard. However, if you'd like to see the RequireSSL
module in action, you'll need to compile the project in Release mode, configure the website in IIS, create a self-signed SSL certificate for it, and add the HTTPS binding for it. If you need some help setting up SSL in IIS7, you can refer to this article: Enabling SSL on IIS 7.0 Using Self-Signed Certificates.
That's it, and it is how Bigshot Hotshot enforces secure pages and HTTP <=> HTTPS.
Happy coding!
History
- 22/02/2010: Initial release.