What is a Token

A token is a kernel object that caches part of a user’s security profile, including the user SID, group SIDs, and privileges (WhatIsAPrivilege). WhatIsSecurityContext discusses the basics of how this cache is normally used, but there’s a bit more to it: A token also holds a reference to a logon session (WhatIsALogonSession) and a set of default security settings that the kernel uses.

Tokens are propagated automatically as new processes are created. A new process naturally inherits a copy of the parent’s process token. Even if the thread that creates the process is impersonating, the new process gets a copy of the parent’s process token, not the thread token, which usually surprises most people who are new to impersonation (WhatIsImpersonation). If you want to start a new process running with some other token, see HowToRunAProgramAsAnotherUser.

The .NET Framework provides two classes that allow you to work with tokens: WindowsIdentity and WindowsPrincipal (WhatIsWindowsIdentityAndWindowsPrincipal). If you ever want to look at the token for your process, call the static method WindowsIdentity.GetCurrent. This method returns a WindowsIdentity instance that wraps the token that represents the thread’s security context. Normally this will give you the process token, unless your thread happens to be impersonating (a rare exception that you can read about in WhatIsImpersonation). This function is the way to discover your program’s security context as far as the operating system is concerned: It answers the question, Who am I? which is very helpful when trying to diagnose security problems such as being denied access to ACL-protected resources like files. I’d recommend including this user name with any errors that you log.
// here’s a simple example of a log that includes
// information about the current security context
void logException(Exception x) {
IIdentity id = WindowsIdentity.GetCurrent();
log.WriteLine(«User name: {0}», id.Name);
log.WriteLine(«Exception: {0}», x.Message);
log.WriteLine(x.StackTrace);
}

The vast majority of information in a token is immutable, and for good reason! It would be crazy to allow an application to add new groups to its token, for example. But you can change a couple things: You can enable or disable any privileges that happen to be in your token (HowToUseAPrivilege), and you can control the default owner and DACL (WhatIsAnAccessControlList). This latter feature allows your process (or another process running in the same security context, say a parent process) to control the owner and DACL that will be applied to all new kernel objects, such as named pipes, mutexes, and sections, whenever a specific DACL is not provided explicitly to the creation function. For example, these defaults will be used if you call the Win32 function CreateMutex and pass NULL for the LPSECURITY_ATTRIBUTES argument, which is the normal and correct procedure. If you ever need to change these default settings, call the Win32 function SetTokenInformation, but this will be very rare. You see, by default the operating system will set up your token so that the default DACL grants you and SYSTEM full permissions, which is very secure indeed. Usually the only time you want to deviate from this is if you’re going to share an object between two processes running under different accounts, such as between a service process that runs as a daemon (WhatIsADaemon) and a service controller process launched by the interactive user. In that case, see HowToProgramACLs to learn how to programmatically build your own DACL.

Tokens never expire. This makes programmers happy (it would be weird if all of a sudden your process terminated because its token timed out), but it can be dangerous in some cases. For example, nothing stops a server from holding onto client tokens indefinitely once those clients have authenticated. A server running with low privilege is good but keeping a bunch of client tokens in a cache negates all that goodness because an attacker that manages to take over the server process can use the cached client tokens to access resources (WhatIsImpersonation). Fortunately, Kerberos tickets do expire (WhatIsKerberos), so if any of those tokens had network credentials (WhatIsDelegation), they won’t be valid forever.

Occasionally you might want to pass tokens between processes. Say you have factored a server into two processes, a low-privileged process listening on an untrusted network (the Internet) and a high privileged helper process that you communicate with using some form of secure interprocess communication such as COM. If you’ve authenticated a client in your listener process and want your helper process to see the client’s token, you can pass it from one process to another by calling the Win32 API DuplicateHandle. You can obtain the token handle from a WindowsIdentity via its Token property (if you have an IIdentity reference, you’ll need to cast it to WindowsIdentity first).

At some point you might think about passing a token (or its wrapper, a WindowsIdentity) from one machine to another. This is a big no-no in Windows security. A token only has meaning on the machine where it was created, because the groups and privileges in it were discovered based on the combination of a centralized domain security policy and the local security policy of the machine. Local groups and privilege definitions differ across machines, and domain security policy changes if you cross domain boundaries. Even if the operating system were to provide a way to serialize a token for transmission to another machine (it does not), using this «imported» token would lead to incorrect access control decisions! Thus, if a client (Alice, say) has authenticated with a process on one machine, and you want another machine to see Alice’s security context, Alice must authenticate with that other machine. Either she can do this directly or you can delegate her credentials (WhatIsDelegation). In other words, the only way to get a token for Alice on a given machine is to use her credentials to authenticate with a process running on that machine.

While I’m on the subject of the machine sensitive nature of tokens, I should mention that you must never use a token on one machine to perform an access check on an object located on another. For example, resist the temptation to load the security descriptor (WhatIsASecurityDescriptor) for a remote object onto another machine and perform an access check against it using a local token. A token for Alice on machine FOO doesn’t have exactly the same groups and privileges it would have if it were produced on machine BAR, so using a token from FOO in access checks against BAR’s resources is a very bad idea and is a gaping security hole. The correct procedure is to authenticate with a process on the machine hosting the resource and have that process perform the access check. In other words, keep the access checks on the same machine as the resources being protected. Sphere: Related Content

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *