Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Switching Between HTTP and HTTPS Automatically

0.00/5 (No votes)
7 Jul 2005 12  
An article on automatically switching between HTTP and HTTPS protocols without hard-coding absolute URLs.

Automatic Protocol Switching

Important: New Version

Please refer to the new version of this module and article here: Switching Between HTTP and HTTPS Automatically: Version 2.

Introduction

Let's face it. Providing sensitive information on a web site is a risk. Many visitors will not give out that kind of data even if they see that the web site claims security. Many more will certainly not reveal their personal details if the warm-and-fuzzy closed padlock isn't visible in their browser window.

Background

Enter Secure Sockets Layer. SSL is a developer's tool for securing the transmission of data. Whether you are encrypting pages for the checkout area of an e-commerce site or you are protecting the personal statistics that your users supply you for marketing; SSL is ideal. A trusted certificate installed on the web server offers visitors, that good feeling of a secure environment.

There are caveats when implementing a web site that makes use of the HTTPS protocol. I'm not referring to the technical nuances you, or a system administrator, must face when installing a certificate on the server. What about simply adding a link from one page to another page that should be secured?

Those of you who have experience with writing web pages that use SSL probably know where I'm going with this. You cannot switch protocols unless you provide an absolute URL. Therefore, in order to allow a visitor to click on a link that should take them to a secure web page, the reference must be absolute.

https://www.codeproject.com/secure/getsensitiveinfo.asp

To make things worse, many browsers download pages referenced by a relative URL with the same protocol as the last request. So, if you had a link in the above file to another page in the root directory, that you wanted to show with the HTTP protocol, it would also have to be absolute.

<!--
The following will actually be translated as 
https://www.codeproject.com/welcome.asp;
thus, retaining the HTTPS protocol that was last used.
-->
<a href="../welcome.asp">Back to the Welcome Page.</a>

Generally, it is not a good idea to encrypt every single page request with SSL. It makes for slower page serves and more bandwidth usage. It is also more intensive on the server's CPU; something your hosting provider may not be pleased with.

A Solution

Being forced to use absolute URLs for internal links in a web site is less than appealing. The next thing you know, the web site's domain name changes (for any number of reasons) or you have a staging server, which means you have to maintain a separate copy of the site for that set of absolute URLs. It makes much more sense to mark certain files and/or entire directories as "secure". This would allow you the benefit of using relative URLs freely within your web pages. If an existing page needs to be made secure, you simply add it to the list of marked files instead of finding and replacing all links to the page with an absolute URL.

That's where SecureWebPageModule comes in. SecureWebPageModule is a class that implements the IHttpModule interface. HTTP modules give programmers a means of "attaching" to a web application to process its events. It's like descending from the System.Web.HttpApplication class and overriding the application and session events in a Global.asax file. The main difference is you don't have to worry about copying and pasting the same code into the file for every application that is to use it. HTTP modules are simply "linked in" to a web application and become part of the pipeline.

The goal of this security solution is to allow a developer to easily secure a web site without the need to hard-code absolute URLs. This is accomplished by listing the files and/or directories that should be secured by SSL. It only seems natural to have a custom configuration section for this.

Configuration

Simply add a new section to the web.config file of the application to secure. Make sure you add this section outside of the <system.web> section but inside the <configuration> tags. The <secureWebPages> section has one optional attribute: enabled. Include the enabled attribute with a value of true or false. The default value is true which enables the automatic web page security module. Setting this attribute to false indicates that you want the feature disabled. This is ideal for testing on a machine that doesn't have a certificate installed (like your development machine).

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    ...
    <secureWebPages enabled="true">
        ...
    </secureWebPages>
    ...
    <system.web>
        ...
    </system.web>
</configuration>

Next, you need to specify any files and directories to include for automatic security. Files are added via <file> tags. Directories are added with <directory> tags. Both tags require a path attribute. The path attribute should be a relative path to the file or directory that will be processed by the security module. In addition, both tags may include an optional attribute: ignore. Set the ignore attribute to either true or false. The default value is false, signifying that the file or directory should be secured. If you set this attribute to true, the security module will not change a request for that file or a file in that directory.

What does that mean? All application requests are processed by the SecureWebPageModule, when enabled. If the requested file is specified in a <file> tag or it is in a directory that is specified in a <directory> tag of the <secureWebPages> section, it is served to the client securely. That is, the response is redirected to the requested file with the HTTPS protocol if it is not already requested as such. If a request does not have a matching entry in the <secureWebPages> section, the response is redirected to the requested file with the HTTP protocol if it was not requested as such. The only exceptions are requests that are matched with a <file> or <directory> tag that has its ignore attribute set to true. In that case, no protocol switch is performed.

    ...
    <secureWebPages enabled="true">
        <file path="Login.aspx" />
        <file path="Lib/PopupCalendar.aspx" ignore="True" />
        <file path="Members/ViewStatistics.aspx" />
        <directory path="Admin" />
        <directory path="Members/Secure" />
    </secureWebPages>
    ...

The above example enables the SecureWebPageModule that is linked to the accompanying web application. As a visitor clicks links and maneuvers through the web site, the SecureWebPageModule processes each request. If the user requests a file located in the Admin or Members/Secure directories, the request is automatically served with the HTTPS protocol. A request to any file in the root directory, except for Login.aspx, is served via the HTTP protocol regardless of the previous request. Files in the Members directory, other than Members/ViewStatistics.aspx, use HTTP. In the case of Login.aspx and Members/ViewStatistics.aspx, HTTPS is used. Finally, if a client requests the PopupCalendar.aspx file in the Lib directory, the file is served exactly as it was requested.

The developer may have chosen to ignore the Lib/PopupCalendar.aspx file because that page is used by several other pages that have calendar icons which, when clicked, opens a pop-up window to the page, allowing the user to select a date. If one of these icons is clicked on a page that is not secure, the developer wouldn't want the Lib/PopupCalendar.aspx page to be served with SSL. On the other hand, if a calendar icon was clicked on a page in the Admin directory, the developer certainly would not want the Lib/PopupCalendar.aspx page to be presented via HTTP. The user would, most likely, be alerted by their browser that they were leaving a secure area to view the pop-up page. I think I can safely state that most users would lose any feeling of comfort after seeing a warning like that.

The SecureWebPageSectionHandler Class

The SecureWebPageSectionHandler class is extremely important to the SecureWebPageModule class. SecureWebPageModule relies on this class to handle parsing of the <secureWebPages> configuration section in your application's web.config file. This class implements IConfigurationSectionHandler. It provides an implementation of the Create method. This method is implemented by handlers to parse the XML of the configuration section. Please download the source code to see all of the classes used in this solution.

    public object Create(object parent, object configContext, 
       XmlNode section)
    {
        // Create a SecureWebPageSettings object for the settings 

        // in this section

        SecureWebPageSettings Settings = new SecureWebPageSettings();

        // Get the enabled attribute

        if (section.Attributes["enabled"] != null)
        {
            Settings.Enabled = (section.Attributes["enabled"].Value.ToLower()
                   != "false");
        }

        if (Settings.Enabled)
        {
            // Traverse the child nodes

            SecureWebPageCollection SecurePathList;
            string Path;
            bool Ignore;
            foreach (XmlNode Item in section.ChildNodes)
            {
                if (Item.NodeType == System.Xml.XmlNodeType.Comment)
                    // Skip comment nodes (thanks to dcbrower

                    // on CodeProject for pointing this out)

                    continue;
                else if (Item.Name.ToLower() == "directory")
                    // This is a directory path node

                    SecurePathList = Settings.SecureDirectories;
                else if (Item.Name.ToLower() == "file")
                    // This is a file path node

                    SecurePathList = Settings.SecureFiles;
                else
                    // Throw an exception for this unrecognized node

                    throw new SecureWebPageSectionException(string.Format(
                        "'{0}' is not an acceptable setting.", 
                        Item.Name), Item);
            
                // Get the path attribute value

                if (Item.Attributes["path"] != null && 
                    Item.Attributes["path"].Value.Trim().Length > 0)
                {
                    // Get the value of the path attribute

                    Path = Item.Attributes["path"].Value.Trim();

                    // Add leading and trailing "/" characters where needed

                    if (Path.Length > 1)
                    {
                        if (!Path.StartsWith("/"))
                            Path = "/" + Path;
                        if (SecurePathList == Settings.SecureDirectories 
                             && !Path.EndsWith("/"))
                            Path += "/";
                    }

                    // Check for an ignore attribute

                    if (Item.Attributes["ignore"] != null)
                        Ignore = (Item.Attributes[
                           "ignore"].Value.Trim().ToLower() == "true");
                    else
                        Ignore = false;

                    // Add the item to the collection

                    SecurePathList.Add(new SecureWebPageItem(Path, Ignore));
                }
                else
                    // Throw an exception for the missing Path attribute

                    throw new SecureWebPageSectionException(
                       "'path' attribute not found.", Item);
            }
        }

        // Return the settings

        return Settings;
    }

This method parses the XML of the <secureWebPages> section and stores the information in an instance of the SecureWebPageSettings class. The SecureWebPageSettings class contains properties for the enabled attribute and collections for the directories and files to secure as specified in the configuration section. Each item of the collections is represented by the SecureWebPageItem class, which defines properties for the path and ignore attributes of each <file> and <directory> tag.

The SecureWebPageModule Class

The SecureWebPageModule class is an implementation of the IHttpModule interface. IHttpModule defines two methods that must be implemented. The first is the Dispose method. No resources are being used in this implementation; therefore, the method is empty.

    public void Dispose()
    {
        // No resources were used.

    }

The second method is the Init method. This method is implemented to initialize the module. In this case, it "hooks" into the application's BeginRequest event.

    public void Init(HttpApplication Application)
    {
        // Add a reference to the private Application_BeginRequest 

        // handler to the application's BeginRequest event

        Application.BeginRequest += (new EventHandler(
             this.Application_BeginRequest));
    }

Finally, the BeginRequest event handler processes each request of the application.

    private void Application_BeginRequest(Object source, EventArgs e) 
    {
        // Get the settings for the secureWebPages section

        SecureWebPageSettings Settings = 
            (SecureWebPageSettings)ConfigurationSettings.GetConfig(
                 "secureWebPages");

        if (Settings != null && Settings.Enabled)
        {
            // Cast the source as an HttpApplication instance

            HttpApplication Application = (HttpApplication)source;

            // Get the relative file path of the current 

            // request from the application root

            string RelativeFilePath = 
                Application.Request.Url.AbsolutePath.Remove(
                0, 
                Application.Request.ApplicationPath.Length).ToLower();
            if (!RelativeFilePath.StartsWith("/"))
                // Add a leading "/"

                RelativeFilePath = "/" + RelativeFilePath;

            // Intialize the flags

            bool MakeSecure = false;
            bool Ignore = false;

            // Determine if there is a matching file path for 

            // the current request

            int i = Settings.SecureFiles.IndexOf(RelativeFilePath);
            if (i >= 0)
            {
                MakeSecure = true;
                Ignore = Settings.SecureFiles[i].Ignore;
            }

            // Try to find a matching directory path, if no file was found

            i = 0;
            while (!MakeSecure && i < Settings.SecureDirectories.Count)
            {
                MakeSecure = RelativeFilePath.StartsWith(
                    Settings.SecureDirectories[i].Path.ToLower());
                Ignore = Settings.SecureDirectories[i].Ignore;
                i++;
            }

            // Test for the ignore flag

            if (!Ignore)
            {
                // Request a secure/unsecure page as needed

                if (MakeSecure)
                    SSLHelper.RequestSecurePage();
                else
                    SSLHelper.RequestUnsecurePage();
            }
        }
    }

The event handler retrieves the SecureWebPageSettings from the configuration settings. These settings are used to determine a course of action. If security is enabled, the application is used to build a relative path for the currently requested file. Next, the SecureFiles collection of the Settings object is searched for a match with the relative path of the current request. If no matching file was found, the SecureDirectories collection is traversed for a path that matches any parent directory of the current request. Lastly, the Ignore property of the matching item, if any, is tested. If no indication to ignore was present, the request is made as specified. The SSLHelper class is used to secure or "unsecure" a page request. The helper class simply replaces the current request's protocol and redirects the response, if needed.

Adding the Module to an Application

In order for a HTTP module to work with a web application, it must be added to the list of modules used. The machine.config file includes many modules for you. It's up to you to add any additional modules that you want to process your application(s). There are two ways you can add the module.

The first option you have, is to add the module to an individual application. This requires that you edit the web.config file of the application. You will need to add a custom configuration section handler for the <secureWebPages> section and a module addition to the <httpModules> section.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    ...
    <configSections>
        ...
        <section 
            name="secureWebPages" 
            type="Hyper.Web.Security.SecureWebPageSectionHandler, 
            WebPageSecurity" 
            allowLocation="false" />
    </configSections>
    
    ...
    
    <system.web>
        ...
        <httpModules>
            ...
            <add 
                name="SecureWebPage" 
                type="Hyper.Web.Security.SecureWebPageModule, 
                WebPageSecurity" />
        </httpModules>
        ...
    </system.web>
    ...
</configuration>

Your second option is to add the module to all web applications. You will need to make similar modifications to the machine.config file. Editing the machine.config file should only be performed by a knowledgeable person with "Administrator" privileges. Always make a backup of your machine.config file before editing it. If you choose to add the module and configuration section handler to your machine.config file, you should sign the assembly with a strong name and register it in the Global Assembly Cache (GAC). The AssemblyInfo.cs file provided with the project source should have a line near the bottom, that is commented to prevent signing the assembly. To sign the assembly during a compile, un-comment this line.

[assembly: AssemblyKeyFile(@"Key.snk")]

For more information on registering an assembly in the GAC, please refer to the .NET Framework documentation.

Points of Interest

This module was desperately needed for a project I was working on earlier in the year. The web site contained nearly 100 pages and controls in 7 main directories. Some of the directories under the root contained directories as well. It was going to be a nightmare to secure certain areas and pages with absolute URLs.

The ignore attribute and feature was not in my original design and implementation. Not until I implemented the module in the web site, did I see the need for it. The example I provided above, with the PopupCalendar.aspx page is exactly how I stumbled across the problem.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here