SharePoint Config

Ari Bakker's thoughts on customising and configuring SharePoint

Securing the authentication cookie for mixed SSL SharePoint sites

with 5 comments

This is the fifth in a series of posts detailing how to configure a partially SSL secured SharePoint site. This post will cover what is involved in ensuring the authentication cookie is only sent via a secure channel (i.e. SSL/HTTPS). This is best practice for securing forms based authentication but has several impacts that can require some fundamental changes to the site so are worth considering early in the development process.

Ensuring that the authentication cookie is only sent via SSL/HTTPS is easy, we simply add the ‘requireSSL’ attribute to our forms element in the web.config as shown below:

<authentication mode="Forms">
    <forms loginUrl="https://www.company.com/pages/login.aspx" requireSSL="true" />
</authentication>

The impact it has, however, is that the authentication cookie is only sent when we request an HTTPS page (i.e. via SSL). This means that now if we login and then browse to the homepage we appear logged out! This is because the .ASPXAUTH cookie we covered in the first post “Securing mixed SSL sites in SharePoint” is not sent for HTTP requests so ASP.NET cannot authenticate us. As a result HttpContext.Current.Request.IsAuthenticated will be false and the MembershipUser and SPContext.Current.Web.User will be null. This means that we cannot use a range of controls such as the ASP.NET LoginView, the SharePoint SPSecurityTrimmed control, the SharePoint Welcome controls and the ASP.NET and SharePoint navigation controls for rendering permissions based navigation.

To illustrate if we log in via HTTPS and then view the homepage we will see the following:

sharepoint-anonymous

The main issues are that the welcome control shows ‘Sign In’ instead of ‘Welcome username’ and the navigation to the restricted ‘My Account’ site does not show.

What we ideally want is similar to what we configured in the previous example where we used the ASP.NET Login controls to create a personalised welcome control as shown below:

sharepoint-https-login

Rewriting the SharePoint and ASP.NET controls to work without the help of the ASP.NET security framework elements is not a trivial task and requires some thought. There are, however, several ways that we can resolve this issue:

  • Put the entire site under SSL
  • Don’t show information that requires authentication on HTTP pages
  • Ensure all requests after the user logs in are over SSL
  • Store personalisation information in separate cookies that are sent via HTTP

This article will cover the last option as this is often the requirement for public facing SharePoint internet sites. In this post we cover one possible solution to demonstrate the concepts that are required to replicate this functionality but it is not intended for production systems without careful consideration and testing.

Solution overview

This solution involves creating a separate cookie that will be configured to be sent via both HTTP and HTTPS. In this cookie we will store the username of the user when they log into the site so we can display this on HTTP pages.

To create a separate cookie we will need to add custom code to the login method to create this ‘soft authentication’ cookie when a user logs in. We can then use this cookie to display the user name and links relevant to the user on HTTP pages even when they appear to be anonymous. As long as we do not store private or authentication information in this cookie it does not pose a security risk as if an attacker steals this cookie they cannot log in to the site or view any private information (as the secure authentication cookie is still required to view this information.

Step 1- Creating an authentication helper class

softauthentication-classTo enable us to create and manage the custom cookie we will create a ‘SoftAuthentication’ class that contains methods to create and destroy the cookie when we login or logout. We will also create properties to retrieve a flag to indicate whether a user has logged in, and a property to retrieve the name of the currently logged in user.

An example implementation of this class is shown below.

public static class SoftAuthentication
{
    public const string softAuthCookie = "softAuth";
    /// <summary>
    /// Returns true if the user has previously logged in and is now on an unsecure page
    /// i.e. we cannot currently determine if they are logged in via forms authentication
    /// </summary>
    /// <returns></returns>
    public static bool HasLoggedIn
    {
        get
        {
            if (HttpContext.Current.Request.Cookies[softAuthCookie] == null)
                return false;
            else
                return !string.IsNullOrEmpty(HttpContext.Current.Request.Cookies[softAuthCookie].Value);
        }
    }
    /// <summary>
    /// Retrieves the username of the soft authenticated user
    /// </summary>
    /// <returns></returns>
    public static string LoginName
    {
        get
        {
            if (HttpContext.Current.Request.Cookies[softAuthCookie] == null)
                return null;
            return HttpContext.Current.Request.Cookies[softAuthCookie].Value;
        }
    }
    /// <summary>
    /// Perform a soft login and store the username for retrieval via HTTP
    /// </summary>
    /// <param name="value"></param>
    public static void Login(string value)
    {
        if (HttpContext.Current.Response.Cookies[softAuthCookie] == null)
            HttpContext.Current.Response.Cookies.Add(new HttpCookie(softAuthCookie, value));
        else
            HttpContext.Current.Response.Cookies[softAuthCookie].Value = value;
    }
    /// <summary>
    /// Logout the soft authenticated user
    /// </summary>
    /// <param name="value"></param>
    public static void Logout()
    {
        if (HttpContext.Current.Response.Cookies[softAuthCookie] != null)
            HttpContext.Current.Response.Cookies[softAuthCookie].Value = string.Empty;
    }
}

Step 2 – Creating a custom login view control

custom-login-view To allow us to show different information to anonymous or logged in users we can create a control similar to the ASP.NET LoginView that contains templates for each group of users. The main difference is that we will use the SoftAuthentication.HasLoggedIn flag to determine which template to show instead of Request.IsAuthenticated. An example of this is shown below.

[ParseChildren(true), Bindable(false)]
public class SoftLoginView : Control, INamingContainer
{
    // Fields
    private ITemplate _anonymousTemplate;
    private ITemplate _loggedInTemplate;
    // Methods
    protected override void CreateChildControls()
    {
        ITemplate anonymousTemplate = null;
        if ((base.DesignMode || (this.Page == null)) || (!this.Page.Request.IsAuthenticated && !SoftAuthentication.HasLoggedIn))
        {
            anonymousTemplate = this.AnonymousTemplate;
        }
        else
        {
            anonymousTemplate = this.LoggedInTemplate;
        }
        if (anonymousTemplate != null)
        {
            Control container = new Control();
            anonymousTemplate.InstantiateIn(container);
            this.Controls.Add(container);
        }
    }
    protected override void Render(HtmlTextWriter writer)
    {
        this.EnsureChildControls();
        base.Render(writer);
    }
    // Properties
    [PersistenceMode(PersistenceMode.InnerProperty), Browsable(false), DefaultValue((string)null), TemplateContainer(typeof(LoginView))]
    public virtual ITemplate AnonymousTemplate
    {
        get
        {
            return this._anonymousTemplate;
        }
        set
        {
            this._anonymousTemplate = value;
        }
    }
    public override ControlCollection Controls
    {
        get
        {
            this.EnsureChildControls();
            return base.Controls;
        }
    }
    [TemplateContainer(typeof(LoginView)), Browsable(false), DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty)]
    public virtual ITemplate LoggedInTemplate
    {
        get
        {
            return this._loggedInTemplate;
        }
        set
        {
            this._loggedInTemplate = value;
        }
    }
}

Step 3 – Creating a custom login control

We also need to ensure that when the user logs in we call the SoftAuthentication.Login method. The easiest way to do this is to create a custom control that contains an ASP.NET login control and contains this call in the LoggedIn event. An example of how this can be done is shown below.

ASCX file:

<asp:Login ID="Login1" runat="server" onloggedin="Login1_LoggedIn">
</asp:Login>

C# file:

protected void Login1_LoggedIn(object sender, EventArgs e)
{
    MembershipUser user = Membership.GetUser(Login1.UserName);
    SoftAuthentication.Login(user.UserName);
}

Step 4 – Creating a custom welcome control

The next step is to create a custom welcome/login name control that displays the name of the logged in user, additional navigation links and a logout link. This control will call SoftAuthentication.LoginName to retrieve the logged in username, and SoftAuthentication.Logout when the user clicks the logout link. An example implementation is shown below.

ASCX file:

<asp:Literal ID="usernameText" runat="server"></asp:Literal> |
<a href="/my-account/">My Account</a> |
<asp:LinkButton ID="logoutLink" runat="server" onclick="logoutLink_Click">Logout</asp:LinkButton>

C# file:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    string name = SoftAuthentication.LoginName;
    if (!string.IsNullOrEmpty(name))
    {
        usernameText.Text = "Welcome " + name;
    }
}
protected void logoutLink_Click(object sender, EventArgs e)
{
    SoftAuthentication.Logout();
    FormsAuthentication.SignOut();
    Response.Redirect(Request.Url.ToString(), true);
}

Step 5 – Adding controls to the masterpage

Once our controls are in place we can add them to the masterpage as shown below:

<SecureControls:SoftLoginView runat="server">
    <AnonymousTemplate>
        <LoginControls:SoftLogin id="softLogin" runat="server"/>
    </AnonymousTemplate>
    <LoggedInTemplate>
        <LoginControls:SoftWelcome id="softWelcome" runat="server"/>
    </LoggedInTemplate>
</SecureControls:SoftLoginView>

Step 6 – Verifying the solution

We now have a basic set of controls that will allow us to show information about the logged in user without having to send the authentication token over HTTP. Now when we log in we should see our username and links displayed as shown below even though we don’t send the authentication token over HTTP:

sharepoint-http-custom-welcome

To verify that no sensitive data is being transmitted we can use a HTTP monitoring tool such as Fiddler to see detailed requests as shown below (edited for clarity):

1. Logon from the homepage

Request data:

POST https://www.company.com/Pages/default.aspx HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, […]
Referer: http://www.company.com/Pages/default.aspx
Accept-Language: en-GB
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; […]
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: www.company.com
Content-Length: 2856
Connection: Keep-Alive
Cache-Control: no-cache

__SPSCEditMenu=true
&MSOWebPartPage_PostbackSource=
&__REQUESTDIGEST=0xD1[…]
&__VIEWSTATE=%2F[…]
&ctl00%24ctl19%24softLogin%24Login1%24UserName=spuser
&ctl00%24ctl19%24softLogin%24Login1%24Password=pass@word1
&ctl00%24ctl19%24softLogin%24Login1%24LoginButton=Log+In

We can see that this was a POST request to send our login information including username and password to the server. We can also see that it was to a secure page (https) in the URL so this data is being safely transferred via SSL.

Response data:

HTTP/1.1 302 Found
Cache-Control: private
Content-Length: 28370
Content-Type: text/html; charset=utf-8
Location: /Pages/default.aspx
Server: Microsoft-IIS/7.0
X-AspNet-Version: 2.0.50727
Set-Cookie: .ASPXAUTH=43B75379C778EF78385E7025FF100925[..]; path=/; secure; HttpOnly
Set-Cookie: softAuth=spuser; path=/
X-Powered-By: ASP.NET
Date: Wed, 07 Apr 2010 22:12:25 GMT

The response we get back over the SSL connection shows the server passing the .ASPXAUTH (forms authentication) and softAuth cookies back to the user. We can see that the forms authentication cookie now has the ‘secure’ attribute to ensure it is only sent over HTTPS (this is the result of the requireSSL setting). We can also see that our custom ‘softAuth’ cookie is sent back and does not have the ‘secure’ attribute that restricts it to HTTPS connections.

Note the HttpOnly attribute refers to whether the cookie is visible to client side script such as JavaScript.

2. Subsequent HTTP requests

Request data:

GET http://www.company.com/Pages/default.aspx HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, […]
Connection: Keep-Alive
Accept-Language: en-GB
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [..]
Accept-Encoding: gzip, deflate
Pragma: no-cache
Cookie: softAuth=spuser;
Cache-Control: no-cache
Host: www.company.com

If we look at subsequent requests to HTTP pages we can see that the forms authentication cookie is no longer send for these unsecured requests. The only cookie sent is the ‘softAuth’ cookie that we use to display the username. If an attacker were to steal this information all they would be able to do is to see our username – as soon as they attempt to access a page secured via SSL the forms authentication would fail and they would simply be presented with the login page.

Post to Twitter Post to Delicious Post to Digg Post to Reddit Post to StumbleUpon

Written by Ari Bakker

April 7th, 2010 at 11:46 pm

5 Responses to 'Securing the authentication cookie for mixed SSL SharePoint sites'

Subscribe to comments with RSS or TrackBack to 'Securing the authentication cookie for mixed SSL SharePoint sites'.

  1. [...] Securing the authentication cookie. [...]

  2. I followed this tutorial but I have a problem. I’m using the SPContext.Current.Web.CurrentUser object but this seems to be null on the non secure site. How can I get access to it? thanks!

    Dasaev Cerqueda

    29 Oct 10 at 6:37 pm

  3. very very good article but the major drawback is that it does it still missing the authenticated section. any guidance on that.

    Sameer

    4 Aug 11 at 12:16 am

  4. This is what i have been searching for on the net. I will try this tutorial in our sharepoint site.

    Do You know what impact this have on the permission level on site – with according to logged in user? How can sharepoint validate the user accordingly.

    Thanks

    Justin P

    1 Feb 12 at 9:46 am

  5. @Justin, @Dasaev as far as permissions go when the user is viewing the site over HTTP then ASP.NET treats them as anonymous users. Only when users view the site over HTTPS will the ASP.NET authentication cookie be sent and the user permission assignment apply. If you need to check permissions on every page then I would consider making the entire site run under SSL/HTTPS.

    Ari Bakker

    2 Feb 12 at 10:08 pm

Leave a Reply