Search

Locations of visitors to this page

Categories

On this page

Forms Auth & Federated Security (part 2)
Integrating Forms Authentication and Federated Security
Message security knobs

Archive

Blogroll

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 50
This Year: 6
This Month: 1
This Week: 1
Comments: 69

Sign In

 Saturday, April 25, 2009
Saturday, April 25, 2009 1:23:14 PM (GMT Standard Time, UTC+00:00) ( Federation/STS | WCF Security )

Here I talked about an approach that you can use to integrate your legacyJ  forms auth applications with STS and bring them into the world of federated security.   

One major hurdle in implementing this approach is to flow Forms Auth cookie to the STS so that it can authenticate the caller and can issue a token. With wsFederationHttpBinding, you don’t directly talk to the STS rather federation binding talks to the STS as part of your service call.  After wsFederationHttpBinding successfully acquired a token, only then your service is called and token is sent as part of the call. This is good because it hides all the token acquisition/forwarding complexity from you and offers you a simple programming model.

Now in our case, we need to intercept the messages sent by FedBinding to the STS so that we can send our Forms Auth cookie along with the message.

At this point a very brief diagrammatic overview of WCF message security framework will help:

On the client side TokenProvider is responsible for providing tokens to message security layer. There is TokenProvider for each type of type (Usernname, IssuedToken etc).

IssuedSecurityTokenProvider is used when a SAML token is required by the message security layer and this is the guy we need to intercept.

Let’s start by creating a custom ClientCredentials:

public class ClientCredentialsWrapper : ClientCredentials

{

    public ClientCredentialsWrapper()

    {}

    public ClientCredentialsWrapper(ClientCredentials other):base(other)

    {}

    public override SecurityTokenManager CreateSecurityTokenManager()

    {

        return new ClientSecurityTokenManagerWrapper(this);

    }

    protected override ClientCredentials CloneCore()

    {

        return new ClientCredentialsWrapper(this);

    }

}

Next our custom SecurityTokenManager:

class ClientSecurityTokenManagerWrapper : ClientCredentialsSecurityTokenManager

{

    public ClientSecurityTokenManagerWrapper(ClientCredentials parent)

        : base(parent)

    { }

    public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)

    {

        var provider = base.CreateSecurityTokenProvider(tokenRequirement);

        var issuedProvider = provider as IssuedSecurityTokenProvider;

        if (issuedProvider != null)

            issuedProvider.IssuerChannelBehaviors.Add(new MessageInspectorInstallerBehavior());

        return provider;

    }

}

Here I have added endpoint behaviour in the ChannelFactory used by IssuedSecurityTokenProvider to talk to STS.

 

 class MessageInspectorInstallerBehavior : IEndpointBehavior

{

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

    { }

 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

    {

        clientRuntime.MessageInspectors.Add(new CookieFlowMessageInspector());

    }

 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    { }

 

    public void Validate(ServiceEndpoint endpoint)

    { }

}

Here I’m simply installing a MessageInspector which looks like this:

class CookieFlowMessageInspector : IClientMessageInspector

{

    public void AfterReceiveReply(ref Message reply, object correlationState)

    { }

 

    public object BeforeSendRequest(ref Message request, IClientChannel channel)

    {

        object prop = null;

        HttpRequestMessageProperty rmp = null;

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out prop))

            rmp = prop as HttpRequestMessageProperty;

        else

            rmp = new HttpRequestMessageProperty();

 

        rmp.Headers[HttpRequestHeader.Cookie] = FormAuthUtility.GetCookieHeaderFromRequest();

        request.Properties[HttpRequestMessageProperty.Name] = rmp;

        return null;

    }

}

Client code will stay the same and you just have to call just one extension method to hook everything together.

 var proxy = new STSCookieServiceReference.EchoServiceClient();

var certPath = HttpContext.Current.Server.MapPath("~/localhost.cer");

proxy.ClientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(certPath);

 

proxy.EnableIssuedTokenProviderCookieFlow(); 

proxy.Echo("Welcome.");

And this is how extension method is implemented.

public static void EnableIssuedTokenProviderCookieFlow<TChannel>(this ClientBase<TChannel> source) where TChannel : class

{   

    var orignal = source.Endpoint.Behaviors.Remove<ClientCredentials>();

    source.Endpoint.Behaviors.Add(new ClientCredentialsWrapper(orignal));

}

I have attached complete solution with this post. Feel free to download and have a look.

 

 

Download: FormsAuthFedSecurity.zip

Comments [0] | | # 
 Wednesday, April 15, 2009
Wednesday, April 15, 2009 8:42:24 PM (GMT Standard Time, UTC+00:00) ( Federation/STS | WCF | WCF Security )

Here I have talked about a technique to flow Forms Authentication cookie to a WCF service and using it as an authentication credential for the service.  For this to work – service must be running in ASP.net compatibility mode and Forms Authentication must be configured in web.config.

Here is diagrammatic view of a simple case.

In the above diagram, website authenticates users using Forms authentication, which creates a cookie after successful authentication. When ASP.net sites calls the backend service (in response to users action) – it simply passes the Form Auth cookie as part of the call. Now because our service is running in asp.net compatibility mode and Forms Authentication module is also configured in web.config, cookie will successfully be validated and we will see correct identity in the HttpContext.User

Looking good so far. Yes?

I think, this is acceptable when you only have one to two services – however when we talk about SOA – we generally mean larger number of services and this is where this approach becomes clunky primarily due to following two reasons:

·         Every service MUST run under asp.net compatibility mode, which is slower than the default native mode. So you incur some performance plenty.

·         For the cookie flow to work, both participants MUST use the same <machineKey>.  As your share single key among more and more participants – the overall security of solution is getting weaker as key will be duplicated across many places.

So there are some scalability & security issues in the above architecture.

 Let’s see how we can improve this architecture by introducing an STS into picture. The goal here is to extract the authentication mechanism (FormsAuth cookie based) out of the services and move it to STS. Service will simply require a token from STS – it is STS’s responsibility to authenticate user using some mechanism (Forms Auth being the step one).

Here you can see ONLY STS is running in asp.net compatibility mode and services are running under native mode which will result in better performance.

Also now we are only sharing keys (<machineKey>) between two participants (STS  & Website) regardless of the number of services. Services are simply configured to require a SAML token from the STS and are agnostic to the actual client authentication mechanism. We can easily change cookie based authentication with Kerberos without impacting services at all.

Now Step 1 isn’t really possible out of box – why?

Because it’s the wsFederationHttpBinding, which talks to STS rather your proxy (configured to use wsFederationHttpBinding). So setting cookie on the proxy will NOT send it to STS and the solution is to extend WCF security framework to enable cookie flow to the STS.

In next post, I will show how to implement Step 1 while still leveraging wsFederationHttpBinding. For now please see this post for background.

 

 

Comments [1] | | # 
 Monday, February 11, 2008
Monday, February 11, 2008 11:46:31 AM (GMT Standard Time, UTC+00:00) ( WCF Security )

Security in WCF is all about credentials. Credentials are used for authentication; claims extracted from the credentials are used for authorization. If credential contains keys then these are used to provide integrity and confidentiality.

Credentials are supported both at transport level and message level. For message level credentials, WS-Security specification defines the mechanism to encapsulate security token (serialized representation of credentials) in a SOAP message.

When you use message security with WCF there are couple of interesting knobs which I am going to explain in this post. So let’s consider the following snippet as an example.

<message clientCredentialType="UserName"

         establishSecurityContext="false"

         negotiateServiceCredential="false"

         algorithmSuite="Default"/>

·         establishSecurityContext

Setting establishSecurityContext="false" results in one shot message security. In this mode every message is secured independently -- algorithm suits determines what algorithm should be used to secure the message. By default WCF uses RSA-OAEP, which uses a symmetric key to encrypt the message and signature. This symmetric key is then wrapped/transported using the public key from the server certificate. All of this security stuff is contained in every SOAP message.

With one shot message security there are further two options:

o   Not using derived keys

Above explanation was for case in which derived keys were turned off. Some of the other stacks (e.g IBM DataPower) don’t support derived keys yet so in those cases this needs to be turned off. By default wsHttpBinding uses the derived keys and there is no way to turn this off from the config file. You have to use a custom binding, if you need to turn off derived keys. See this for details. By default “derived keys” are turned off in case of basicHttpBinding.

o   Using derived keys

With derived keys turned on, WCF derives two keys from the symmetric key and use one to sign the SOAP message and other to encrypt various parts of the SOAP message. It is considered bad practice to sign and encrypt data using the same key as certain attacks are more likely to succeed in this case so derived keys provide more resilient against such attacks and that’s why its enabled on wsHttpBinding as a default option.

Setting establishSecurityContext="true" results in a “Security Context Token” established between client and server. Once this token is created on both ends – all subsequent message exchanges will be secured using this context token. By default this token is issued with a 15 minute life time and need to be re-issued if required beyond 15 minutes. Again not using derived keys here means multiple messages (exchanged in 15 minutes life-span) will be secured using the same “Security Context Token” so security in this case will be more weaker than one shot security. So use of derived keys is more important in case of secure conversation.

·         negotiateServiceCredential

Credential negotiation is a process of exchanging keys and authenticating communicating parties using some form of handshake. Now credential negotiate can be done at the transport level using something like SSL over HTTPS or can be done at SOAP level using TLSNego (SSL over SOAP).

Setting negotiateServiceCredential="false" means that service credentials should be made available to the client using some out-of-band mechanism. Here starts the complexity as WCF supports many different types of credentials and out-of-band availability mechanism is credential specific.

So in case of Certificate credentials, service certificate should be provided to all the clients prior to communication and clients should refer the service certificate in their security configuration.

<clientCredentials>

  <serviceCertificate>

    <defaultCertificate findValue="localhost"

      storeLocation="LocalMachine" storeName="My"

      x509FindType="FindBySubjectName"/>

    <authentication certificateValidationMode="PeerOrChainTrust" />

  </serviceCertificate>

</clientCredentials>

In case of Kerberos credentials, setting negotiateServiceCredential="false" results in on-shot Kerberos or sometimes called Kerberos direct. For this mode to work the service must be running under machine SPN (NetworkService, LocalService accounts etc) and client must specify the SPN in the endpoint identity.

<endpoint address="http://zamd.net/servicemodelsamples/service.svc"

          binding="customBinding"

          bindingConfiguration="Binding1"

          behaviorConfiguration="ClientCredentialsBehavior"

          contract="Microsoft.ServiceModel.Samples.ICalculator" >

  <identity>

    <servicePrincipalName value="host/zamd.net"/>

  </identity>

</endpoint>

Setting negotiateServiceCredential="true" will results in credentials negotiation. Now as mentioned earlier actual negotiation depends on client and service credential type and could either be done at transport level or message level (using appropriate credential exchange protocols) depending on your security mode. Let’s take this example:

<wsHttpBinding>

  <binding name="Binding1">

    <security mode="Message">

      <message clientCredentialType="UserName" negotiateServiceCredential="true"/>

    </security>

  </binding>

</wsHttpBinding>

The above binding will result in UserNameForSslNegotiated credential type -- which uses UserName as client credential type and certificate as service credential and service credentials will be negotiated using SSL over SOAP aka TLSNEGO.

Comments [5] | | #