Programming with Credentials
Writing code to deal with password-based credentials can introduce many subtle bugs into your code. Having a good understanding of credentials is important in writing robust and correct code, not to mention secure code. In this piece I will focus on what makes up a set of credentials and how you should interpret them as a provider and consumer of credentials.
Firstly, password-based credentials involve three pieces of information:
The entity that manages the account and provides authentication services. In Windows parlance this typically refers to a domain, but can also refer to the Security Account Manager (SAM) that manages local user accounts on a computer.
A security principal is an entity that can be identified via authentication. A principal's account is managed by an authority. Principals typically represent users but can also represent computers when the computers are part of a domain.
The secret associated with a principal's account used for authentication.
Often people speak in terms of a "user name". This is not a strong term but generally refers to a string containing both the authority and principal names. A user name is most often expressed in one of three forms:
This is commonly referred to as the SAM, or Windows NT, format since it is the format traditionally used by Windows NT. On Windows 2000 and later the Authority name is specified using the domain's down-level name.
For example: KENNYANDKARIN\kenny
This is referred to as the User Principal Name (UPN) and was introduced with Windows 2000.
For example: firstname.lastname@example.org
When the user provides only a principal name and no authority, the program needs to make some decisions on how to interpret it. Typically the authority is assumed to be the computer that is being accessed unless there is some other authority indicator in a user interface such as a combo box with a list of authorities.
For example: kenny
When dealing with credentials explicitly, it is common to use the System.Net.NetworkCredential class. The nice thing about this class is that internally it encrypts the credentials so a bag guy trying to steal the credentials by scanning your address space somehow, or possibly reading the swap file, cannot gain access to the clear text credentials. On the downside, the NetworkCredential class makes it easy to manage credentials incorrectly. Here are the properties of the NetworkCredential class and their descriptions from the MSDN Library:
Gets or sets the domain or computer name that verifies the credentials.
Gets or sets the password for the user name associated with the credentials.
Gets or sets the user name associated with the credentials.
As I said before, most people think of a user name as referring to both the principal and authority names. To make it worse, the NetworkCredential class offers a constructor that populates only the UserName and Password properties:
public NetworkCredential(string userName, string password);
I have noticed that many developers simply populate the UserName property with a SAM or UPN user name. This would be fine if there were not also a Domain property. A better approach is to simply consider the UserName property the Principal name and the Domain property the Authority name. That way there is no ambiguity from the consuming end whether the authority name should be taken from the UserName property or from the Domain property.
Of course, given a populated NetworkCredential object, library authors cannot make any assumptions about how the object was populated. If Domain is not a blank string you should assume this is the name of the authority. In this case the UserName property cannot contain an @ or \ character as this would introduce ambiguity. If Domain is blank then you need to determine whether the authority name is embedded in the UserName property.
Once you have deciphered the credentials, there may still be some complications depending on what you are going to do with them. If you are going to authenticate the principal yourself then it should be simple. If however you need to pass the credentials to another security subsystem, then you need to understand its requirements for expressing credentials. This is where you just need to be very careful. Read the documentation carefully for the functions you are calling and make sure you test your code with appropriate test cases.
© 2004 Kenny Kerr