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

Switching Between HTTP and HTTPS Automatically: Version 2

0.00/5 (No votes)
7 Feb 2011 997  
An article on automatically switching between HTTP and HTTPS protocols without hard-coding absolute URLs

This project is now being maintained on GitHub under the new name “Security Switch”. All updates will be posted and tracked there. This article will remain here for educational purposes, but all issues should be reported at its new location. A new version of this library is also available from NuGet

PM> Install-Package SecuritySwitch

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.

See the What's New section for the latest updates.

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 that 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 website 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

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    ...
    <secureWebPages

        mode="RemoteOnly"

        encryptedUri="secure.mysite.com"

        unencryptedUri="www.mysite.com"

        maintainPath="True"



        warningBypassMode="AlwaysBypass"

        bypassQueryParamName="BypassSecurityWarning"

        ignoreHandlers="WithStandardExtensions">

        ...
    </secureWebPages>

    ...
    <system.web>

        ...
    </system.web>
</configuration>

 

Attribute and Possible ValuesDescription
mode
On (Default)Security is enabled and all requests are monitored.
RemoteOnlyOnly requests from remote clients are monitored.
LocalOnlyOnly requests from the local server are monitored.
OffNo requests are monitored.
The mode determines when the module will monitor requests for redirection.
encryptedUriSet to a specific URI to indicate where to redirect when the module decides that security is needed.
unencryptedUriSet to a specific URI to indicate where to redirect when the module decides that security is not needed.
maintainPath
True (Default)When redirecting to the specified URIs (encryptedUri and unencryptedUri), the current path is maintained.
FalsePrevents the module from maintaining the current path when redirecting to the specified URIs (encryptedUri and unencryptedUri).
This flag indicates whether or not the module should maintain the current path when redirecting to encryptedUri or unencryptedUri.
warningBypassMode
AlwaysBypassAlways bypass security warnings when switching to an unencrypted page.
BypassWithQueryParam (Default)Only bypass security warnings when switching to an unencrypted page if the proper query parameter is present.
NeverBypassNever bypass security warnings when switching to an unencrypted page.
Use this attribute to bypass any security warnings that may be displayed.
bypassQueryParamNameSet to the name of a query parameter that will indicate to the module to bypass any security warning if warningBypassMode = "BypassWithQueryParam". The default value is "BypassSecurityWarning".
ignoreHandlers
BuiltIn (Default)The built-in HTTP handlers should be ignored. Currently, these are Trace.axd and WebResource.axd.
WithStandardExtensionsAll files that have an extension that corresponds to standard HTTP handlers should be ignored. Currently, that is *.axd files.
NoneNo HTTP handlers should be ignored unless specifically specified in the files or directories entries.
This attribute is used to instruct the module to ignore HTTP handlers. This is only used in version 3.x and above.

mode behaves like the customErrors mode attribute. Use it to avoid redirecting to the HTTPS protocol on your development or production machine when you don't have a secure certificate installed.

The encryptedUri and unencryptedUri attributes are perfect for those situations where your secure certificate is associated with a different site than the one needing security. I know; examples are better than just explaining it. Okay then, visit Sears Mastercard and notice the security alert that appears. If you are using Internet Explorer, you should see a security alert window appear, warning you that something is wrong with the site's certificate. Everything is okay except the name doesn't match the name of the site. Click on the View Certificate button and note that the site listed next to "Issued to." "www.searscard.com" is the site they purchased the certificate for.

What does this mean? Well, the site should be www.searscard.com. However, the IT folks at Sears thought it would be a good idea to purchase the "searsmastercard.com" domain as well as allow for another point of entry. Both DNS records point to the same place on their Web server. They have a few options that would prevent this alert from displaying to their users, but two are the most obvious. They can redirect to https://www.searscard.com when users visit the default page on www.searsmastercard.com or they could upgrade their site to ASP.NET and download this module. All they'd have to do then is set the encryptedUri attribute to www.searscard.com and all would be wonderful.

Likewise, unencryptedUri may be specified to send the user back to another domain or specific URI when the module removes security. An example would be to redirect secure requests to secure.mysite.com and requests that don't need to be secure could be redirected back to www.mysite.com. maintainPath is used in conjunction with the aforementioned attributes. When the module redirects to the encryptedUri or unencryptedUri, it appends the current path before sending users on their way. You can turn off this behavior by setting maintainPath to False.

In certain circumstances, Internet Explorer displays the message, "You are about to be redirected to a connection that is not secure." This only happens as a result of a "double redirect" to an unsecured page. That is, when a page is requested via HTTPS, the programmer's code performs a relative redirect and then the module performs an absolute redirect via the HTTP protocol.

Use the warningBypassMode attribute to bypass the security warning as desired. If you choose to bypass security warnings via the default BypassWithQueryParam option, you may specify the name of the query parameter with the bypassQueryParamName attribute. Use this when you only want to run the bypass code on the rare occasions when you know the security warning will appear, and request the page with the query parameter specified. An example of this is when your code will be posting back to a secure page for server-side processing and then redirecting to a page that the module will deem unsecured. Simply redirect to the page and append the bypassQueryParamName with any value like this:

Response.Redirect("MyUnsecurePage.aspx?BypassSecurityWarning=True");

I received a couple of suggestions on how to solve the above warning. There was one suggestion that involved a configuration attribute that would point the module to a "redirector page." This page would be sent a parameter containing the page that should be redirected to and it would change the location via meta refresh and JavaScript as a backup. The idea is a good one. I just don't like making the user of this module create a page that has preset code in it. Therefore, if the module determines that it should bypass the warning, it will render the necessary page itself, complete with meta tag and JavaScript. This will cause a client-side redirect and avoid the security warning.

One power of ASP.NET is the ability to create custom HTTP handlers that act similarly to this module. The handlers are invoked when a certain file or type of file is requested from the server. In ASP.NET 2.0, embedded resources make heavy use of the WebResource.axd virtual file to dynamically serve images and JavaScript that don't actually have a physical file. When used, these handlers may cause mixed security warnings unless the module is instructed to ignore them. The ignoreHandlers attribute lets you generally ignore these handlers quite easily. You may configure the module to ignore any standard HTTP handler with a file extension of *.axd by setting the attribute to WithStandardExtensions. The default setting is BuiltIn and forces the module to just ignore the two built-in handlers Trace.axd and WebResource.axd.

Now... on to the file and directory entries.

secureWebPages for .NET 1.1

...
<secureWebPages>

    <file path="Default.aspx" secure="Insecure" />

    <file path="Admin/MoreAdminStuff.aspx" secure="Insecure" />

    <file path="Legal/Copyright.aspx" secure="Ignore" />

    <file path="Lib/PopupCalendar.aspx" secure="Ignore" />

    <directory path="/" recurse="False" />

    <directory path="Admin" />

    <directory path="Admin/Info" secure="Insecure" />
    <directory path="Members/Secure" recurse="True" />

</secureWebPages>

...

secureWebPages for .NET 2.0

...
<secureWebPages>
    <files>
        <add path="Default.aspx" secure="Insecure" />

        <add path="Admin/MoreAdminStuff.aspx" secure="Ignore" />

        <add path="Legal/Copyright.aspx" secure="Ignore" />

        <add path="Lib/PopupCalendar.aspx" secure="Ignore" />

    </files>
    <directories>
        <add path="/" recurse="False" />

        <add path="Admin" />

        <add path="Admin/Info" secure="Insecure" />
        <add path="Members/Secure" recurse="True" />

    </directories>

</secureWebPages>

...

Notice that you can now include the application root as a directory entry. There is no longer an ignore attribute for each entry. It has been replaced by the secure attribute. This attribute tells the module how to handle that particular file or directory. The default value is Secure, which simply means that the module should redirect to the HTTPS protocol when that file or a file in that directory is requested.

Setting the attribute to Insecure will force a matching request to be served without SSL. In the example above, although the application root is secured, the Default.aspx page in the application root should not be. The secure attribute may also have a value of Ignore, which mimics the functionality of version 1's ignore attribute. Any request to a file with a matching file or directory entry marked with secure="Ignore" will be ignored by the module. This is good when a page should remain in the same protocol as the last request, such as pop-up windows used by both secure and unsecured pages.

Another example of this is an ASPX page that is used to serve content other than HTML and is referenced from within a secure page. There are times when an ASPX page will serve an image or a style sheet and is included by a secure page with the appropriate <img> or <link> tag. In these cases, a visitor of such a page would receive a warning that there is a mixture of secure and unsecured items. That usually doesn't make users feel too good about a page. Just include an entry for the file and set secure to Ignore, and there will be no problems.

You may also provide the recurse attribute for directory entries. Setting this attribute to True will inform the module to include all files in any sub-directories when monitoring requests.

Adding the Module to Applications

There are two options for adding the module to your applications. The first 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.

configSections for .NET 1.1

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    ...
    <configSections>
        ...
        <section

            name="secureWebPages"



            type="Ventaur.Web.Security.SecureWebPageSectionHandler,
            WebPageSecurity"

            allowLocation="false" />

    </configSections>

    ...


</configuration>

configSections for .NET 2.0

<?xml version="1.0"?>

<configuration>
    ...
    <configSections>

        ...
        <section

          name="secureWebPages"



          type=
            "Ventaur.Web.Security.Configuration.SecureWebPageSettings,
          WebPageSecurity" />
    </configSections>

    ...

</configuration>

httpModules on IIS 6.x and Earlier or IIS 7 in "Classic" Mode

<?xml version="1.0" encoding="utf-8" ?>

    <system.web>

        ...
        <httpModules>

            ...
            <add

                name="WebPageSecurity"

                type="Ventaur.Web.Security.SecureWebPageModule,
                    WebPageSecurity" />

        </httpModules>
        ...
    </system.web>

    ...
</configuration>

modules on IIS 7.x and Later in "Integrated" Mode

<?xml version="1.0" encoding="utf-8" ?>
    <system.webServer>

        ...
        <modules>

            ...
            <add

                name="WebPageSecurity"

                type="Ventaur.Web.Security.SecureWebPageModule,
                    WebPageSecurity"

                preCondition="managedHandler" />

        </modules>
        ...
    </system.webServer>

    ...
</configuration>

The second option is to add the module to all Web applications on the server. 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 handlers 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.

IIS 7

With the appearance of IIS 7, an "Integrated" mode is now available that integrates the IIS pipeline with our Web applications. This requires a new configuration approach as shown above. I recommend using the preCondition attribute when adding this module so it will only be instantiated for typical ASP.NET resources (like *.aspx, *.ashx, etc.). Those of you that want the module to process all requests, including static files (like *.htm, *.jpg, *.css, etc.), leave this pre-condition out, but be aware that you may have to add a bit of extra file/directory path entries to this module's configuration section in order to get the desired results you want. For example, if you are getting warnings about mixed secure and non-secure content on a page that you designate as secured, check for images and style sheets that are being forced to use HTTP. In those cases, add file and/or directory entries that tell the module to ignore these resources when processing.

Notes

Please be aware that although IIS allows you to "Require a secure channel (SSL)" for a folder's "Directory Security," this module will not work properly if you do so. IIS will intercept the request before passing it along to the module and reject insecure connections. Therefore, if you want to use this module, you do not require SSL from IIS.

Also, testing this module on a development machine without an installed SSL certificate will yield unexpected results. The browser may appear to "hang" or fail altogether. This is because it is being sent to a page that should be encrypted, but is not.

You can get rid of the warnings regarding missing schema information and get IntelliSense for the <secureWebPages> configuration section by following a few easy steps:

  1. Copy the appropriate schema file (Ventaur.WebPageSecurity.v3x.xsd or Ventaur.WebPageSecurity.v2x.xsd) into the root of your WebPageSecurity source code (or the folder of your liking; maybe a common schema folder on your computer).
  2. Open up a website that is using this module in Visual Studio (or the IDE of your choice that supports IntelliSense).
  3. Open the web.config file for the website.
  4. Click "Schemas" from the "XML" menu or click the ellipsis button next to "Schemas" in the Properties window for the web.config file.
  5. Click the "Add..." button in the XML Schemas window that appears.
  6. Browse to the schema file you copied in step 1 and select it (open it).
  7. Make sure it is in the list and has a green check next to it in the first column, labeled "Use". If not, click in that column next to it (target namespace is "http://Ventaur/WebPageSecurity.v3x.xsd"), and choose "Use this schema" from the drop-down list.
  8. Click the "OK" button to close the dialog.
  9. Finally, if you already have a secureWebPages section defined in your configuration, add this attribute to the tag, xmlns="http://Ventaur/WebPageSecurity.v3x.xsd" (substitute v2x for v3x if you are using version 2.x of this module).

Your configuration section for this module should then look like this:

<secureWebPages xmlns="http://Ventaur/WebPageSecurity.v3x.xsd" mode="RemoteOnly" ...>
    ...
</secureWebPages>

Version History

  • Version 3.1
  • Version 2.6
  • Version 3.0
  • Version 2.5
    • Bug fix: encryptedUri/unencryptedUri domain differing from the current request domain. For example, if unencryptedUri="www.mysite.com", and the user visits a page via mysite.com, 4 characters (www.) were dropped when the path was maintained during a redirect. This bug was squashed
    • See what's new for more details
  • Version 2.1
    • Changed the maintainApplicationPath attribute to maintainPath
    • Improved the code that redirects to another host via the encryptedUri and unencryptedUri
  • Version 2.0
  • Version 1.0.1
    • Added code to skip comments in the configuration file. Thanks goes to dcbrowser
    • Minor performance changes to select areas of the code
  • Version 1.0.0
    • Initial release

Download file for binaries (all versions) last updated on 11th December, 2008.

What's New

Version 3.1.4 and 2.6.4

  • Added a schema file for the <secureWebPages> configuration section to offer IntelliSense and no warnings when building websites that use the module.
  • Made sure that the 'xmlns' attribute is allowed by the configuration section code.

Version 3.1.3 and 2.6.3

  • Moved the event handler that processes the request from the application's BeginRequest event to the PostAcquireRequestState event. This ensures that the session ID is available for cookie-less session processing.

Version 3.1.2 and 2.6.2

  • Added the BeforeEvaluateRequest event to allow subscribers the ability to cancel the evaluation of the current request. Create a handler in Global.asax defined as:
    public void WebPageSecurity_BeforeEvaluateRequest
    	(object sender, Ventaur.Web.Security.EvaluateRequestEventArgs e)

Version 3.1

  • Added the ignoreHandlers attribute to generally ignore HTTP handlers. I normally do not like setting the default of a new setting to something that is different from the previous version's default functionality, but I believe it is acceptable in this case. The default setting is BuiltIn to force the module to ignore requests for Trace.axd and WebResource.axd
  • The module now accounts for cookie-less sessions when redirecting without a specified encryptedUri or unencryptedUri
  • A tiny change was made to the RequestEvaluator.Evaluate method that allows you to force an evaluation, despite the mode setting in the configuration. This should allow for better testing and simulation

Version 3.0

  • Ported to .NET 2.0
  • Configuration re-written to conform to the new configuration API

Version 2.6

  • The module now accounts for cookie-less sessions when redirecting without a specified encryptedUri or unencryptedUri.
  • A tiny change was made to the RequestEvaluator.Evaluate method that allows you to force an evaluation, despite the mode setting in the configuration. This should allow for better testing and simulation.

Version 2.5

  • Separated appropriate logic into static helper classes. This adds benefit for testing purposes. Now, simple testing can be achieved by simulating the processes.
  • Removed the configuration restriction of "order matters" for directory elements. Now, directory elements may be included in the configuration in any order. The module processes directory matches with the attitude that the deepest match is the best match. In other words, if a request is made for a page located in Admin/Reports/ and the configuration file has a directory element for both Admin/ and Admin/Reports/, it no longer matters if Admin/ is first. Before, the module would stop once it found a match, despite recursion settings. Now, it finds the most accurate (deepest) match. Thanks goes out to balazs_hideghety for getting me started with this feature update.

Version 2.0 and 2.1

  • Source code provided in C# and VB.NET
  • The configuration settings loader is now called once in an application Init event handler, and if the settings indicate not to use security, a handler for the BeginRequest event is not added. This improves the overall performance of the module and any application including it. Thanks goes to Diego
  • The root directory of the virtual application can now be secured by including a directory tag with a path of "/". Thanks to Andy for pointing this one out
  • A mode attribute was added to the secureWebPages section. Possible values are On, RemoteOnly, LocalOnly or Off. Thanks to Andy for this suggestion
  • The encryptedUri and unencryptedUri attributes were added to the secureWebPages section. Set to a specific URI to indicate where the module should redirect when it decides whether security is needed or not. Thanks to Andy for this suggestion
  • The maintainPath attribute was added to the secureWebPages section. A value of False will prevent the module from maintaining the current path when redirecting to any specified URIs
  • The warningBypassMode attribute was added to the secureWebPages section. Possible values are AlwaysBypass, NeverBypass or BypassWithQueryParam
  • The bypassQueryParamName attribute was added to the secureWebPages section. Set the value to the name of a query string parameter that indicates to the module to bypass any security warning if warningBypassMode is set to BypassWithQueryParam
  • The ignore attribute was removed from the file and directory tags
  • The secure attribute was added to the file and directory tags. Possible values are True, False or Ignore
  • directory tags may include the new recurse attribute. If True, all files in any sub-directories are included

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