Click here to Skip to main content
Click here to Skip to main content

Switching Between HTTP and HTTPS Like A Bigshot Hotshot

By , 22 Feb 2010
 

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 Pages 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 Attributes to mark which Pages 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 Pages that require SSL with that Attribute. Let's define an Attribute called RequireSSL:

/// <summary>
/// Attribute decorated on classes that use SSL
/// <summary>
[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):

/// <summary>
/// The Login Page
/// </summary>
[RequireSSL]
public partial class login : System.Web.UI.Page
{
    ...
}

/// <summary>
/// The SignUp Page
/// </summary>    
[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:

/// <summary>
/// HttpModule for switching between HTTP and HTTPS (HTTP <=> HTTPS)
/// </summary>
public class RequireSSLModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
// only attach the event if the build is not set to Debug
#if !DEBUG
        // The PreRequestHandlerExecute event occurs just
        // before ASP.NET begins executing a handler such as a Page
        // In here we can acquire a reference
        // to the currently executing ASPX Page
        context.PreRequestHandlerExecute += 
           new EventHandler(OnPreRequestHandlerExecute);
#endif
    }
    
    ...
}

Let's take a closer look at the PreRequestHandlerExecute event.

/// <summary>
/// Handle switching between HTTP and HTTPS.
/// It only switches the scheme when necessary.
/// Note: By scheme we mean HTTP scheme or HTTPS scheme.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void OnPreRequestHandlerExecute(object sender, EventArgs e)
{
    // obtain a reference to the ASPX Page 
    System.Web.UI.Page Page = 
       HttpContext.Current.Handler as System.Web.UI.Page;

    // if a valid Page was not found, exit
    if (Page == null)
    {
        return;
    }

    // check if the Page is decorated with the RequireSSL attribute
    bool requireSSL = (Page.GetType().GetCustomAttributes(
                         typeof(RequireSSL), true).Length > 0);

    // check if the Page is currently using the HTTPS scheme
    bool isSecureConnection = 
      HttpContext.Current.ApplicationInstance.Request.IsSecureConnection;

    // acquire the URI (eg~ http://localhost/default.aspx)
    Uri baseUri = HttpContext.Current.ApplicationInstance.Request.Url;

    // if the Page requires SSL and it is not currently
    // using HTTPS, switch out the scheme
    if (requireSSL && !isSecureConnection)
    {
        // switch the HTTP scheme to the HTTPS scheme
        string url = baseUri.ToString().Replace(
                         baseUri.Scheme, Uri.UriSchemeHttps);

        // perform a 301 redirect to the secure url
        PermanentRedirect(url);
    }
    // if the page does not require SSL and it is currently
    // using the HTTPS scheme, switch out the scheme
    else if (!requireSSL && isSecureConnection)
    {
        // switch the HTTPS scheme to the HTTP scheme
        string url = baseUri.ToString().Replace(baseUri.Scheme, 
                                                Uri.UriSchemeHttp);

        // perform a 301 redirect to the non-secure url
        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:

  1. The first is that the code has been optimized to only HTTP <=> HTTPS when necessary.
  2. 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.
  3. 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Pero Matić
Software Developer (Senior) Plan A Software
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionExcellant! Just what I needed!memberMember 79968662 May '13 - 19:13 
GeneralMy vote of 5memberIvaylo5ev8 Nov '12 - 1:27 
QuestionExcelent Articlemembereberetta6 Feb '12 - 4:55 
QuestionIncluding Form Variablesmemberbossman7516 Nov '11 - 7:15 
GeneralMy vote of 5memberdahhak22 May '11 - 13:42 
GeneralProblem using IIS7memberTomb0127 Apr '11 - 4:13 
GeneralYou da man! Thank you. Had a problem though but fixed it.memberMember 43120034 Jan '11 - 1:09 
GeneralExcellent. But OnLoad is firing twicemembereerwin2 Jan '11 - 22:19 
GeneralMy vote of 5memberDaniel Cohen Gindi22 Dec '10 - 0:43 
GeneralGreat SolutionmemberShellySS1 Dec '10 - 8:59 
GeneralSimple and sweetmemberdjvoracious29 Sep '10 - 23:06 
GeneralCould not load type 'Code.Site1'memberDan Dougherty16 Jul '10 - 5:46 
GeneralExcellent work Pero...membertonnyk2 Jun '10 - 17:16 
GeneralExcelent!memberPablo Castillo17 May '10 - 9:56 
GeneralDifferent approachmemberPaulie-D16 Apr '10 - 17:23 
GeneralCould not load file or assembly....memberDerek Howard25 Mar '10 - 10:41 
GeneralRe: Could not load file or assembly....memberDerek Howard25 Mar '10 - 11:55 
GeneralRe: Could not load file or assembly....memberdafaust9 Aug '10 - 9:56 
GeneralRe: Could not load file or assembly....memberMember 43120034 Jan '11 - 1:12 
QuestionHow do you get past IIS returning a 403.4? [modified]memberdgk818215 Mar '10 - 8:14 
AnswerRe: How do you get past IIS returning a 403.4?memberPero Matić15 Mar '10 - 12:11 
GeneralRe: How do you get past IIS returning a 403.4?memberdgk818216 Mar '10 - 4:17 
GeneralRe: How do you get past IIS returning a 403.4?memberPero Matić16 Mar '10 - 5:31 
GeneralRe: How do you get past IIS returning a 403.4?memberdgk818217 Mar '10 - 8:11 
GeneralNot able to add the AssemblyInfo.csmemberSakthivel Ganesan15 Mar '10 - 1:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 23 Feb 2010
Article Copyright 2010 by Pero Matić
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid