A .NET Developer's Guide to Windows Security: Understanding Delegation
Before Windows embraced Kerberos in Windows 2000, a simple challenge-response authentication protocol called NTLM was in place. Basically this said that to verify Alice's identity, Bob would challenge her by sending a unique number that she would then encrypt with a master key derived from her password. Bob would then send the challenge and Alice's response to a domain controller for verification. Bob didn't know Alice's password, and after this exchange he still didn't know her password. He therefore had no possible way to impersonate Alice on the network because, when Charlie challenged Bob to encrypt a number with Alice's password, Bob couldn't respond. The only way to make this work would be for Alice to tell Bob her password, and that would be very dangerous indeed because passwords are long-term secrets. There would be no practical constraint over Bob's use of Alice's password, either in space or in time. Bob could impersonate Alice to any server for months on end. This is why delegation simply was not supported in Windows NT 4 domains.
But along came Windows 2000 with Kerberos. If you read Item 59 (and I recommend that you do before you continue reading this item), you know that a Kerberos credential isn't the client's password. It's a temporary "ticket" plus a corresponding session key, and that ticket has a lifetime of a single workday. So in Windows 2000 you can flip a switch called "Trust this computer for delegation . . ." in Active Directory, and a server will suddenly be allowed to receive these types of delegated credentials. In fact, Active Directory even advertises this to clients: If Bob is a server account trusted for delegation, any tickets for Bob issued to clients contain a flag: ok-as-delegate.
Let's say you set up an IIS server on an intranet and configure it to use Integrated Windows Authentication (Kerberos). Let's say you also mark that computer account as "trusted for delegation" in Active Directory. When Alice points Internet Explorer to that Web server, during authentication the browser will see the ok-as-delegate flag and will check to see if Alice has a "forwardable" ticket (technically this is a ticket-granting ticket) for her domain authority. This will be the case only if Alice's account hasn't been marked "sensitive and cannot be delegated" in Active Directory. If she has a forwardable ticket, the browser will ask her domain authority for a "forwarded" ticket with a new session key and send that off to the Web server (the session key will be encrypted so an eavesdropper can't use it). The Web server can now use Alice's Kerberos credential (ticket plus session key) to obtain tickets to any other server on the network in her name. A Web programmer doesn't notice any of this magic happening. All he knows is that he can now impersonate his client (Alice) and access remote resources as her! Remember that tickets have a limited lifetime, and this delegated ticket will only be valid for ten hours from when Alice first logged in. So, in essence, Windows 2000 provides delegation that's constrained in time but not in space.
Let's back off of the mechanics for a minute and think about what this means. Before delegation, we had some serious limitations in how we could design multitier systems. Because the middle-tier server couldn't delegate the client's credentials to the back end, the back end had to trust the middle tier to perform any access checks that were necessary (see Figure 62.1). Think about it: No matter who the real client happened to be (Alice or somebody else), the back end always saw the middle tier's identity (Bob). This was one of the main motivations for Microsoft Transaction Server (MTS), which provided a framework for doing these access checks in the middle tier in Windows NT 4, before delegation was supported. That framework was called role-based security, and it still exists today in the successor to MTS, COM+.
Figure 62.1 A three-tier system without and with delegation
With delegation, things look a bit different. Now the middle tier (Bob) can pass through the client's identity directly to the back end. Whereas the middle tier can still perform some role-based access checks, the back end has ultimate authority and can perform very fine-grained access control based on the original client's level of authorization.
There are pros and cons to either scenario. Without delegation, the middle tier can use pooled connections to access the back end. This is faster than warming up a connection for every client. But think about what happens if the middle tier is compromised! Because the middle tier uses its own credentials (Bob) to do work on behalf of all clients, an attacker who compromises it can do anything Bob can do with that back-end server. The attacker can run any stored procedure that Bob can run, can view any tables that Bob can view, and can make any changes that Bob is allowed to make. The back end trusts Bob, so if Bob falls to an attacker, the back end falls immediately as well.
With delegation, the middle tier (Bob) has very little privilege on the back end (possibly none at all), for the clients are the ones granted permissions to the back-end server. If the middle tier is compromised, the attacker has to wait for individual users to connect so he can impersonate them and use their credentials to attack the back end. But each individual user presumably has access only to a very small set of data on the back end. In this scenario, if the middle tier falls to an attacker, the back end is still quite insulated because it doesn't intrinsically trust the middle tier. One problem with this picture is that in Windows 2000 delegation isn't constrained in space. So if the middle tier is attached to servers other than the back end with which it's designed to work, the attacker might use clients' delegated credentials to attack these other servers. This problem is solved in Windows Server 2003, as you'll see. The other problem is that the middle tier can no longer use connection pooling to talk to the back end. You need a new connection for each user you impersonate. (Remember what a database connection is: It's an authenticated connection to the database!)
I happen to think using a mix of the two approaches in Figure 62.1 is your best bet. If you categorize interactions with the back end into low-privileged and high-privileged, you can allow the middle tier to perform all low-privileged interactions using Bob's credentials, making the best use of pooled connections. A high-privileged interaction requires the original client's credentials to be delegated to the back end. If the middle tier is compromised, the attacker has immediate access only to the interactions categorized as low-privileged. Damage is limited in the short term, and your detection countermeasures (Item 2) can kick in and notify the administrator that there's a problem. Not all systems can be built this way, but it's certainly a design worth considering.
Another thing to consider when delegating client credentials is that because the back end now grants permissions directly to clients as opposed to the middle tier, what's to stop the client from connecting directly to the back end instead of going through the middle tier? You probably don't want to allow this, but when talking to a traditional server like SQL Server, there's really no way to constrain this other than using conventional techniques such as firewalls. This is one place where I see Web services coming to the rescue. Because the Web service security specs are so flexible, there's no stopping the middle tier from passing two sets of credentials to a Web service back end: his own credentials (Bob) and those of his client (Alice). The back end can do a quick check to make sure Bob's credentials are valid and then use Alice's credentials to perform authorization. This prevents Alice from going directly to the back end, because she doesn't have Bob's credentials! I think it's an interesting idea to consider when building new systems.
Windows Server 2003 makes delegation more palatable by constraining it not only in time but also in space. I show the delegation options in a Windows Server 2003 domain in Figure 62.2. In Windows 2000 there were only two options for allowing an account to delegate client credentials:
-
Don't allow this account to delegate client credentials.
-
Allow this account to delegate client credentials to any server on the network.
Figure 62.2 Constrained delegation in Windows Server 2003
Windows Server 2003 domains add a third option: Allow delegation to specified services only. This mitigates the problem of an attacker who has taken over the middle tier and wants to use delegated client credentials to attack servers that the middle tier wasn't even designed to talk to. With an extension to Kerberos, Windows Server 2003 domain authorities can now constrain delegating servers by restricting whom they can talk to using delegated credentials. In Figure 62.2, the middle-tier server is configured so that it can delegate client credentials, but only to SQL Server running on a machine called DATASERVER. This feature is known as "constrained delegation," and to learn more about how it works under the hood, see Brown (2003).
Delegation is an important security feature in Windows, one that requires virtually no programming (well, you have to impersonate to make it happen, but that's it). It's really a design issue, and one that should be considered early in the life cycle of an N-Tier project. Used carefully, delegation can help slow down an attack. Used carelessly, it can lead to middle-tier servers teeming with juicy client credentials that can be used to attack other network servers. See Item 64 to learn how to use delegation.