Friday, February 6, 2009

SSL for only some pages in SharePoint

I had a client that wanted SSL for only a few of the pages on their website, Login Page, all Checkout pages, and Account Management pages. But they didn't want SSL on all the other pages because they want the pages to load as fast as possible.

After much research I found one blog that really helped me get this accomplished.
http://sanmuki.blogspot.com/2008/07/enable-ssl-for-selected-pages-in-moss.html

I'll list below the steps I needed to take to make this happen.

Usually you have to find a way to handle the session state when switching between HTTP and HTTPS, but MOSS already has that ability.

1. First create a class that will be used to check if URLs need to be SSL'ed or not and redirect URLs as needed.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Security;
using System.Diagnostics;
using System.Web.UI;

namespace Project.Business
{
public class ForceSSL : System.Web.IHttpModule
{
#region IHttpModule Members

public void Dispose()
{
//throw new NotImplementedException();
}

public void Init(System.Web.HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}

#endregion

void context_BeginRequest(object sender, EventArgs e)
{
RedirectSSL(System.Web.HttpContext.Current.Request.Url.PathAndQuery);
}

/// <summary>
/// Method to switch between SSL and non-SSL.
/// </summary>
/// <param name="pageURL">URL that needs to be looked up for SSL requirement.</param>
/// <returns></returns>
public void RedirectSSL(string pageURL)
{
//---------------------------------------------------
// Init VARs
//---------------------------------------------------
bool found = false;
//--- Get Pages from web.config that need to be SSL'ed
string sslPages = System.Configuration.ConfigurationSettings.AppSettings["SSLPages"];

if (!(null == sslPages))
{
//---------------------------------------------------
// Parse out the list of Pages that need SSL
//---------------------------------------------------
char[] separator;
separator = new char[] { ',' };
string[] pages;
pages = sslPages.Split(separator);
for (int i = 0; i < pages.Length; i++)
{
//---------------------------------------------------
// Check if URL passed to method is in the list
//---------------------------------------------------
if (pageURL.ToLower().Contains(pages[i].ToLower()))
{
found = true;
break;
}
}
}

if (found) //-- If page needs to be SSL
{
//---------------------------------------------------
// If page needs to be SSL but is not already set as SSL
//---------------------------------------------------
if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "http")
{
//---------------------------------------------------
// Init VARs. Get the SSLPort from web.config
//---------------------------------------------------
string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
string sPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
string sslPort =System.Configuration.ConfigurationSettings.AppSettings["SSLPort"];
//-- getting rid of the double slashes to make this process easier.
sURL = sURL.Replace("http://", "http");
//-- if SSL is on port 443, then the :443 is not needed, but anything else it is need (i.e. :444)
if ((null == sslPort) || sslPort == "443")
{
sslPort = "";
}
else
{
sslPort = ":" + sslPort;
}
//-- if coming from a http port other than 80 then replace with new port else add the new port
if (sURL.IndexOf(":") > 0)
{
sURL = sURL.Replace(":" + sPort, sslPort);
}
else
{
sURL = sURL.Substring(0, sURL.IndexOf("/")) + sslPort + sURL.Substring(sURL.IndexOf("/"));
}
//-- add back the double slashes and include the "s" to http
sURL = sURL.Replace("http", "https://");
//-- debugging to know it was redirected
Debug.WriteLine("Redirected SSL URL: " + sURL);
//-- redirect to the new URL
System.Web.HttpContext.Current.Response.Redirect(sURL);
}
}
else //-- If pages needs not be SSL
{
//---------------------------------------------------
// If page needs not be SSL but is SSL currently
//---------------------------------------------------
if (System.Web.HttpContext.Current.Request.Url.Scheme.ToString() == "https")
{
//---------------------------------------------------
// Init VARs. Get the non-SSL port from web.config
//---------------------------------------------------
string sURL = System.Web.HttpContext.Current.Request.Url.ToString();
string sslPort = System.Web.HttpContext.Current.Request.Url.Port.ToString();
string sPort = System.Configuration.ConfigurationSettings.AppSettings["NonSSLPort"];
//-- getting rid of the double slashes to make this process easier.
sURL = sURL.Replace("https://", "https");
//-- if non-SSL is on port 80, then the :80 is not needed, but anything else it is need (i.e. :81)
if ((null == sPort) || sPort == "80")
{
sPort = "";
}
else
{
sPort = ":" + sPort;
}
//-- if coming from a http port other than 443 then replace with new port else add the new port
if (sURL.IndexOf(":") > 0)
{
sURL = sURL.Replace(":" + sslPort, sPort);
}
else
{
sURL = sURL.Substring(0, sURL.IndexOf("/")) + sPort + sURL.Substring(sURL.IndexOf("/"));
}
//-- add back the double slashes and exclude the "s" from https
sURL = sURL.Replace("https", "http://");
//-- debugging to know it was redirected
Debug.WriteLine("Redirected non-SSL URL: " + sURL);
//-- redirect to the new URL
System.Web.HttpContext.Current.Response.Redirect(sURL);
}
}
}
}
}


2. Build this code, sign the assembly and put it in the GAC.

3. Add lines to web.config...

<httpModules>
<add name="ForceSSL" type="Cook.Business.ForceSSL, Cook.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx"/>
</httpModules>



<appSettings>
<!-- Switching between HTTP/HTTPS -->
<add key="SSLPort" value="443"/>
<add key="NonSSLPort" value="2222"/>
<add key="SSLPages" value="/Pages/UserLogin.aspx,/Checkout/CheckoutPayment.aspx,/Checkout/CheckoutShipping.aspx,/Checkout/CheckoutReview.aspx,/Checkout/CheckoutConfirmation.aspx"/>
</appSettings>



4. We need an SSL certificate in order to use SSL.
  a. If you already have an SSL certificate on the server move on to #5
  b. To create your own certificate you can use SelfSSL from Microsoft
    b1. Download the IIS 6.0 Resource Kit Tools
    b2. Install the tools on the server
    b3. Run SelfSSL. Make sure you put the length of time out farther than a week (my mistake) so it will last as long as your project will last.

5. Now we need to set up the website in IIS for SSL.
  a. Go to Start -> Administrative Tools -> Internet Information Services (IIS) Manager
  b. Expand the IIS server
  c. Expand the Web Sites folder
  d. Go to properties of the website
  e. Inside properties, navigate to Directory Settings -> Secure Communications -> Server Certificate
  f. Assign the existing certificate. This is from #4

6. Inside Directory Security, go to secure communications -> edit, and make sure Require secure channel (SSL) is NOT checked.

7. On the Web Site tab (same level as Directory Security) add an SSL port. The default is 443. If you use 80 and 443 for TCP port and SSL port respectively then your URL will not need the ports mentioned in the URL, but the code above handles that if you do.

8. The post that I learned this from needed one more step that I did not need. ***OK I was wrong. I did not need this on my development server, but I did on the live server. The difference seems to be FQN (Fully Qualified Name). Without this step, I'd get sqlsessionstateresolver errors much of the time on the live server.
  a. Go to the Central Administration website
  b. Go to Operations -> Alternate Access Mappings
  c. Add the new URL via internal URL's link.
https://servername/

9. Perform an IISReset
  a. Go to a command prompt and type iisreset

You should now be able to go on to your website and see the pages in your web.config list are SSL while everything else is not SSL.

No comments:

Post a Comment