Switching between HTTP and HTTPS protocols without hard-coding absolute URLs
This project is now being maintained on Google Code 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 the Google Code site: http://code.google.com/p/securityswitch/.
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 Values | Description | ||||||||
mode
|
The mode determines when the module will monitor requests for redirection. | ||||||||
encryptedUri |
Set to a specific URI to indicate where to redirect when the module decides that security is needed. | ||||||||
unencryptedUri |
Set to a specific URI to indicate where to redirect when the module decides that security is not needed. | ||||||||
maintainPath
|
This flag indicates whether or not the module should maintain the current path when redirecting to encryptedUri or unencryptedUri . |
||||||||
warningBypassMode
|
Use this attribute to bypass any security warnings that may be displayed. | ||||||||
bypassQueryParamName |
Set 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
|
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.
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:
- 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). - Open up a website that is using this module in Visual Studio (or the IDE of your choice that supports IntelliSense).
- Open the web.config file for the website.
- Click “Schemas” from the “XML” menu or click the ellipsis button next to “Schemas” in the Properties window for the web.config file.
- Click the “Add…” button in the XML Schemas window that appears.
- Browse to the schema file you copied in step 1 and select it (open it).
- 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.
- Click the “OK” button to close the dialog.
- 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>
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)