Quantcast
Channel: MSDN Blogs
Viewing all articles
Browse latest Browse all 12366

FedAuth cookie encryption

$
0
0

The issue

 

If you are using SharePoint with AD FS, you may know how these two products interact. Thefollowingsummarizestheloginprocess:

  1. When the user is not logged in, SharePoint will redirect the user to the AD FS login page.
  2. The user enters his credentials.
  3. AD FS validates the credentials and creates a Security Token with the user's claims.
  4. AD FS makes the user's browser or client POST this token to SharePoint's /_trust endpoint.
  5. SharePoint validates the digital signature on the token and stores the token in the Distributed Cache.
  6. SharePoint creates a FedAuth cookie with some information about the session. The contents of the cookie are Base 64 encoded and sent to the client. Inside the cookie we can find the user identity claim, the scope, expiration and a couple more things.

For example, this is the content of a FedAuth cookie in my lab.

 

 <?xmlversion="1.0" encoding="utf-8"?><SP>05.t|adfs email|a1@email.local,05.t|adfs email|a1@email.local,130866957906607856,True,[signature],https://adfs.2013.sps/</SP>

 

Depending on the configuration of the AD FS provider in SharePoint, one of the claims in the AD FS token will be used as identity claim in SharePoint. This claims will be used to uniquely identify the user in SharePoint and thus it's very important to choose a property of the user that's unique. Most organizations choose the e-mail address or the User-Principal-Name.

 

Some organizations may want to use a different identity claim like the social security number or another fiscal information of the user. These organizations may consider this kind of information sensitive and they even may want to protect this information.

 

Since anybody can decode Base64, anybody can see the contents of the FedAuth cookie. In the one above, I have highlighted the identity claim. One way to protect this information would be to encrypt the value of the cookie.

 

The solution

 

SharePoint does not offer cookie encryption out of the box so if you want to encrypt this value, custom code needs to be created.

 

One possible solution could be a custom HttpModule that will encrypt the cookie when set by SharePoint and decrypt it when SharePoint receives it.

 

The custom HttpModule would attach to:

  • BeginRequest: to decrypt any FedAuth cookie that may come with the client's request.
  • EndRequest: to encrypt any FedAuth cookie that may be set by SharePoint to the client.

 

How should we encrypt the value?

 

I'm my opinion, a symmetric algorithm would be ideal over an asymetric. Symmetric algorithms are usually faster and don't require a key pair to work, just a password. How to choose a proper password and salt would be the interesting part. It has to be a password/salt combination that doesn't change between application pool recycles and both need to be the same in every web server. As an example, the code below uses a password and salt stored in the web.config of the application.

 

The code

 

The code consists of two parts: the custom HttpModule and a utility class to handle the encryption. This utility class I got from the following link: http://www.superstarcoders.com/blogs/posts/symmetric-encryption-in-c-sharp.aspx

 

This should be compiled to a DLL. The easiest way to install it would be to copy the DLL into the bin folder of the web application and then add the module with IIS Manager.

 

HttpModule

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Security.Cryptography;

using System.Text;

using System.Web;

using System.Web.Configuration;

 

namespace jesusfer.SharePoint.FedAuthEncryptionModule

{

    /// <summary>

    /// This is a HttpModule that will encrypt the value of a certain cookie when it's set by the server and will decrypt it when sent by the client.

    ///

    /// This is helpful in cases when the FedAuth cookie includes an identity claim that can be considered sensitive.

    /// </summary>

    public class FedAuthEncryptionModule : IHttpModule

    {

        private string m_CookieName = "FedAuth";

 

        // If true, unencrypted FedAuth cookies will still be allowed.

        // If false, unecrypted FedAuth cookies will be removed, which will force SPS to ask the user to authenticate again.

        private bool m_AllowUnencryptedCookies;

        private bool m_Enabled;

        private string m_Password;

        private string m_Salt;

 

        /*

         * These are the expected settings keys in the web.config:

         *

         * <configuration>

         * ...

         *    <appSettings>

         *       <add key="FedAuthEncryptionEnabled" value="true"/>

         *       <add key="FedAuthEncryptionPassword" value="FedAuthEncryptionPassword"/>

         *       <add key="FedAuthEncryptionSalt" value="FedAuthEncryptionSalt"/>

         *       <add key="FedAuthEncryptionAllowUnencryptedCookies" value="true"/>

         *    </appSettings>

         * ...

         * </configuration>

         * */

 

        public String ModuleName

        {

            get { return "CookieEncryptModule"; }

        }

 

        public void Dispose() { }

 

        public void Init(HttpApplication application)

        {

            // Read configuration

            m_Enabled = bool.Parse(WebConfigurationManager.AppSettings["FedAuthEncryptionEnabled"]);

            m_AllowUnencryptedCookies = bool.Parse(WebConfigurationManager.AppSettings["FedAuthEncryptionAllowUnencryptedCookies"]);

            m_Password = WebConfigurationManager.AppSettings["FedAuthEncryptionPassword"];

            m_Salt = WebConfigurationManager.AppSettings["FedAuthEncryptionSalt"];

 

            // Attach to the BeginRequest event so that we can check if the cookie is encrypted to decrypt it

            application.BeginRequest += new EventHandler(this.Application_BeginRequest);

            // Attach to the EndRequest event so that we can encrypt the coookie when it's set by SharePoint

            application.EndRequest += new EventHandler(this.Application_EndRequest);

        }

 

        /// <summary>

        /// This method will encrypt the cookie when it's set by SharePoint

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void Application_EndRequest(object sender, EventArgs e)

        {

            HttpApplication application = (HttpApplication)sender;

            HttpContext context = application.Context;

 

            if (m_Enabled && context.Response.Cookies.AllKeys.Contains(m_CookieName))

            {

                try

                {

                    // Try encrypting the cookie value and setting it as the new value

                    string plain = context.Response.Cookies[m_CookieName].Value;

                    string encrypted = CipherUtility.Encrypt<AesManaged>(plain, m_Password, m_Salt);

                    context.Response.Cookies[m_CookieName].Value = encrypted;

                }

                catch

                {

                    // Do nothing

                }

            }

        }

 

        /// <summary>

        /// This method will decrypt the cookie when sent by the client

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void Application_BeginRequest(object sender, EventArgs e)

        {

            HttpApplication application = (HttpApplication)sender;

            HttpContext context = application.Context;

 

            // m_enabled can be added as a condition here too, but that will break any FedAuth cookie that is encrypted and still valid

            if (m_Enabled && context.Request.Cookies.AllKeys.Contains(m_CookieName))

            {

                try

                {

                    // Try decrypting the cookie value and setting it as the new value

                    string encryped = context.Request.Cookies[m_CookieName].Value;

                    string decrypted = CipherUtility.Decrypt<AesManaged>(encryped, m_Password, m_Salt);

                    context.Request.Cookies[m_CookieName].Value = decrypted;

                }

                catch

                {

                    // We can choose to remove the cookie if decryption failed

                    if (!m_AllowUnencryptedCookies)

                    {

                        context.Request.Cookies.Remove(m_CookieName);

                    }

                }

            }

        }

    }

}

 

Utility Class

 

using System;

usingSystem.Collections.Generic;

using System.IO;

usingSystem.Linq;

usingSystem.Security.Cryptography;

usingSystem.Text;

 

namespacejesusfer.SharePoint.FedAuthEncryptionModule

{

    /// This class was copied from http://www.superstarcoders.com/blogs/posts/symmetric-encryption-in-c-sharp.aspx

 

    public class CipherUtility

    {

        public static string Encrypt<T>(string value, string password, string salt) where T : SymmetricAlgorithm, new()

        {

            DeriveBytesrgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

 

            SymmetricAlgorithm algorithm = newT();

 

            byte[] rgbKey = rgb.GetBytes(algorithm.KeySize>> 3);

            byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize>> 3);

 

            ICryptoTransform transform = algorithm.CreateEncryptor(rgbKey, rgbIV);

 

            using (MemoryStream buffer = new MemoryStream())

            {

                using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))

                {

                    using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))

                    {

                        writer.Write(value);

                    }

                }

 

                return Convert.ToBase64String(buffer.ToArray());

            }

        }

 

        public static string Decrypt<T>(string text, string password, string salt) where T : SymmetricAlgorithm, new()

        {

            DeriveBytesrgb = new Rfc2898DeriveBytes(password, Encoding.Unicode.GetBytes(salt));

 

            SymmetricAlgorithm algorithm = newT();

 

            byte[] rgbKey = rgb.GetBytes(algorithm.KeySize>> 3);

            byte[] rgbIV = rgb.GetBytes(algorithm.BlockSize>> 3);

 

            ICryptoTransform transform = algorithm.CreateDecryptor(rgbKey, rgbIV);

 

            using (MemoryStream buffer = new MemoryStream(Convert.FromBase64String(text)))

            {

                using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Read))

                {

                    using (StreamReader reader = new StreamReader(stream, Encoding.Unicode))

                    {

                        returnreader.ReadToEnd();

                    }

                }

            }

        }

    }

}

 


Viewing all articles
Browse latest Browse all 12366

Trending Articles