
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 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
<?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
On (Default) |
Security is enabled and all requests are monitored. |
RemoteOnly |
Only requests from remote clients are monitored. |
LocalOnly |
Only requests from the local server are monitored. |
Off |
No requests are monitored. |
|
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
True (Default) |
When redirecting to the specified URIs (encryptedUri and unencryptedUri), the current path is maintained. |
False |
Prevents 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
AlwaysBypass |
Always 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. |
NeverBypass |
Never bypass security warnings when switching to an unencrypted page. |
|
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
BuiltIn (Default) |
The built-in HTTP handlers should be ignored. Currently, these are Trace.axd and WebResource.axd. |
WithStandardExtensions |
All files that have an extension that corresponds to standard HTTP handlers should be ignored. Currently, that is .axd files. |
None |
No 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 searsmastercard and notice the security alert that appears. If you are using IE, 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 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 to 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 the user on their way. You can turn off this behavior by setting maintainPath to "False".
In certain circumstances, IE 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, 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 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 so: 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 similar 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 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 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 for .NET 1.1 and 2.0
<?xml version="1.0" encoding="utf-8" ?>
<system.web>
...
<httpModules>
...
<add
name="WebPageSecurity"
type="Ventaur.Web.Security.SecureWebPageModule,
WebPageSecurity" />
</httpModules>
...
</system.web>
...
</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.
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, 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.
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
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 to not 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.
About Matt Sollars
|
I began programming on my Commodore 64 at around the age of 12. After migrating to DOS and then Windows, I decided to take on the Web. Several languages and platforms later, I have settled in with .NET nicely. I am currently the lead developer for a small company that offers its services to high-end clients.
The love of a finished application is usually at war with the desire to improve it as soon as it's released (they're never really finished). |