Update to SharePoint SSL Switching HttpModule
I recently wrote about a Combined SSL HttpModule for SharePoint Webs and Pages that we used on a project to switch a SharePoint site in and out of SSL for certain _layouts pages as well as specific webs within the site collection.
Turns out there was a flaw in the HttpModule that appeared when I was using newer builds of FireFox and IE8. With the HttpModule enabled, I would get browser warnings that my entire connection wasn't encrypted.
FireFox handled this somewhat gracefully, putting a red exclamation mark over the padlock icon. IE8, on the other hand prompted the user if they would like to load the page without the items in question, causing the page to barf <-- technical term.
After spending a lot of time analyzing the traffic with Fiddler (a must in every developer's toolkit), as well as a very helpful suggestion on StackOverflow, I figured out the cause of the issue.
When analyzing the traffic in Fiddler, you could see that the request started as SSL, but was followed by a 301 response, redirecting it to a non-HTTPS link to the resource I was retrieving.
This was happening for items I had in my master page (using relative links!), e.g. my CSS, JavaScript, and general images for look and feel. Interestingly, IE6, IE7, and Chrome ignored the error and showed my site as incorrectly running full SSL. Odd given that I now understood that some resources were in fact not being transmitted over SSL.
Obviously the HttpModule will fire even for requests for "non-HTML" content. Knowing this, I modified the HttpModule to ignore that type of content.
Check out the original post for most of the background code, here's the updated PreRequest handler:
HttpContext ctx = HttpContext.Current;
SPContext spContext = SPContext.Current;
if (spContext != null)
{
if (spContext.Web != null)
{
// Assume that request does not require SSL
bool requestRequiresSSL = false;
// Check if the current request is for page content
// not gif, js, css, etc.
bool isHTMLContent = !_ignoreExt.Contains(
Path.GetExtension(ctx.Request.PhysicalPath).ToLower());
// Get the request Path and QueryString value
string pathAndQuery = ctx.Request.Url.PathAndQuery;
if (isHTMLContent)
{
// Layouts pages need to be handled separately
//
if (pathAndQuery.ToLower().Contains("_layouts"))
{
// Check if the current layouts page requires SSL
foreach (var page in _sslPages)
{
if (pathAndQuery.ToLower().Contains(page.ToLower()))
{
requestRequiresSSL = true;
break;
}
}
}
else
{
// Check if the current site requires SSL
if (spContext.Web.Properties.ContainsKey
(SITE_REQUIRES_SSL_PROPERTY))
requestRequiresSSL = (spContext.Web.Properties
[SITE_REQUIRES_SSL_PROPERTY].ToString()).ToLower()
== Boolean.TrueString.ToLower();
else
return;
}
}
if (isHTMLContent)
{
// SSL required and current connection is not SSL
if (requestRequiresSSL && !ctx.Request.IsSecureConnection)
ctx.Response.Redirect(_baseURLWithSSL + pathAndQuery);
// SSL not required but current connection is SSL
if (!requestRequiresSSL && ctx.Request.IsSecureConnection)
ctx.Response.Redirect(_baseURLNoSSL + pathAndQuery);
}