Category Archives: ADFS

Customized claims in ADFS

Introduction

The claims pipeline in ADFS is an interesting piece of software. I recently had a chance to re-familiarize myself with it. A third party SaaS application used an organizations internal employee numbers together with their own customer number for that organization to uniquely identify users. This called for issuing a claim to the SaaS app relying party (a.k.a. service provider) that picked up an attribute from Active Directory containing the internal employee numbers, prepending the SaaS app’s customer number and issuing it as a Name ID claim. Furthermore it was a requirement that the Name ID claim was the only custom claim issued. Of course I wanted the most elegant and efficient solution I could come up with, so that meant the the number of claims rules had to be as low as possible.

To do this kind of thing you have to use custom claim rules. The template rules are not flexible enough, but it is a good idea to use them to create the base claims query language syntax for you. Here is what I ended up with:

Get the employeeID LDAP attribute from Active Directory

c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”, Issuer == “AD AUTHORITY”]
=> add(store = “Active Directory”, types = (“http://langskip.no/employeeID”), query = “;employeeID;{0}”, param = c.Value);

This claim rule queries the Active Directory store for the employeeID attribute. If it is present a claim is added to the incoming claims pipeline by using the operator ADD. I store the value of employeeID in a custom type (https://langskip.no/employeeID) which only exists as a temporary placeholder for the value of employeeID. You can use both URLs and URIs to create custom claim types, if you don’t want to go with one of the standard ones. No claim is issued by this rule. That happens in the next rule…

Transform employeeID

c:[Type == “http://langskip.no/employeeID”]
=> issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier”, Value = “350-00” + c.Value);

Next we check for the existence of an incoming claim of type http://langskip.no/employeeID. If it is present we now issue a claim of type nameidentifier. If the statement evaluates to False; no claim is issued. Hopefully the relying party knows what to do in that case. We set the value of the Name ID claim to the SaaS app’s customer ID number plus the employeeID from Active Directory.

The result looks like this in a test app I used for testing:

Claim Type Claim Value
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier 350-00123456

Thoughts on improvements…

I really would have wanted to accomplish this with just one claim rule. If anyone of you reading this knows how to accomplish that; sound off in the comments.

Happy authenticating!

How not to improve the security of your ADFS deployment

Introduction

I was involved in an ADFS deployment recently where the customer wanted to restrict access from the Internet to their ADFS proxy servers, located on their DMZ. They used ADFS to federate with Windows Azure Active Directory so they only wanted to allow traffic from the Microsoft Online Security Token Service (STS) servers into their ADFS. The rational behind this was that only a trusted party (Microsoft) should be able to communicate with an externally available service in their network. A good theory, but one doomed to failure. Let me explain why…

The WS-Federation Passive Requestor Profile

ADFS is Microsoft’s implementation of the OASIS group’s WS-* suite of protocols and mechanisms. A complete description of WS-*  is way beyond this post, but I will list some resources at the end of this post for the inquisitive among you. One of the purposes of the WS-* standard is to allow:

“different security realms to federate, such that authorized access to resources managed in one realm can be provided to security principals whose identities and attributes are managed in other realms”

In other words, to outsource authentication to somebody else that you trust.

The letters WS stand for Web Service, meaning that WS-* is created to work on the Internet, something e.g. Kerberos is not designed to do. Let’s look at a few pieces of WS-* terminology:

  • Requestor
    The client that wants access to some resource/content/service. Often a browser. Requestors are broadly split into two groups; those that can emit Web Service messages using SOAP and those that cannot. (Remember this last bit because it is important.)
  • Relying party (RP)
    The content provider that has something that a user wants access to. The RP has delegated the task of authentication to somebody else; the Identity Provider (IdP)/Security token Service (STS). The RP trusts the IdP/STS. In the Windows world an RP is often an IIS server with the Windows Identity Foundation (WIF) framework installed.
  • Identity Provider (IdP)
    A secure storage of identity information that also provides authentication mechanisms, allowing a Security Token Service (STS) to use it to authenticate. Active Directory is ADFS’ IdP.
  • Security Token Service (STS)
    A service that receives authentication requests from clients, authenticates them via its configured IdP, and issues tokens to clients, to be used at the RP.
  • Trust
    A relationship between the RP and the IdP/STS established with digital certificates whereby the RP trusts the IdP/STS.

A browser cannot emit web service requests, i.e. it cannot talk SOAP, so it uses something called the WS-Federation Passive Requestor Profile (PRP). WS-Federation is an extension to the WS-Trust specification that allows requestors that cannot talk SOAP to still exchange security information (tokens). They do this by being passive, meaning they rely on the RP and IdP to tell them what to do.

An implementation of this can look like this:

image

This is what happens when the passive requestor profile is used to obtain access to a resource:

  1. The requestor contacts the resource asking for access. The resource is the RP.
  2. The RP know the requestor’s security realm so it sends an HTTP redirect instructing the requestor to authenticate at its own realm.
  3. The requestor follows the redirect to its own realm where it is authenticated by its IdP/STS.
  4. In the request for authentication the requestor has included the URI of the resource it wants to access to. After the IdP/STS has authenticated the requestor, it sends another HTTP redirect back to the original resource. Included with the redirect is also a token, possession of which assures that the user has been authenticated.
  5. The requestor follows the redirect back to the original resource supplying the token and gains access to the resource.

The most important thing to note here is that when using the Passive Requestor Profile the RP and IdP/STS never need to communicate directly. It is only the requestor that needs to talk to all the involved parties.

Usually the RP is also an IdP/STS. In the case of Windows Azure Active Directory and federation with an on-premise Windows Server Active Directory the WAAD IdP/STS trusts your on-premise IdP/STS. This allows a single trust to be established. If not each RP would have to maintain its own trust with the IdP/STS.

So now that we know how the Passive Requestor Profile works how would this impact my customer’s request to only allow Microsoft’s STS to talk to its ADFS servers?

Security

Answer is that it would break it and no one would be able to access any resource in the Microsoft cloud. As we have seen it is not the WAAD STS that communicates with on-premise ADFS, or the other way around, it is the requestors, i.e. the clients. So restricting access would deny every client access to on-premise ADFS, and thus any resource they want to access. Don’t do that.

Other stuff and more info

The customer had actually started working on this “security improvement” before I got involved and had already discovered IPs they needed to allow. For the curious, the name of the Microsoft STS is login.microsoftonline.com, which is a CNAME that resolves to the A record login.microsoftonline.com.nsatc.net. The A record has several records (I do not know if this is a complete list):

  • 157.56.53.213
  • 157.56.58.13

To restrict access and always use least privilege is a very good idea, in this case it just backfired because how the system works was not known.

If you want to know more about WS-*, ADFS etc., you can have a look at these resources:

Be secure!

Norwegian content: How to integrate your on-premise Active Directory with Windows Azure Active Directory

I have published a 5 part blog series on the Norwegian Microsoft TechNet Blog, with step by step instructions for setting up integration between your on-premise Window Server Active Directory Directory Service and Windows Azure Active Directory. It covers concepts, single-sign on with ADFS, Directory Synchronization with the DirSync Tool and troubleshooting. So if you speak my native language; head on over:

Morgan