Security Briefs

Security Enhancements in the .NET Framework 2.0

Keith Brown

Contents

System.Security
System.Security.Principal
System.Security.AccessControl
System.Net.Security
System.Runtime.Remoting
System.Security.Cryptography
System.Security.Cryptography.X509Certificates and PKCS
System.Security.Cryptography.Xml
System.Diagnostics
System.DirectoryServices
System.DirectoryServices.ActiveDirectory
System.Web
Changes in Code Access Security
Wrapping It Up

Version 2.0 of the Microsoft®.NET Framework has been released. A year ago, when I first got my hands on the Beta 2 bits, I hacked together a little program to dump all of the public members of all public types in the entire Framework and ran it on version 1.1 in addition to version 2.0. I then used WINDIFF.EXE to compare the two text files, and spent a few hours paging through the changes taking notes, paying special attention to anything that was security related.

Security support in the .NET Framework got a lot of love in version 2.0, and this column is going to take you on a whirlwind tour of the goodies you'll find there. I won't be able to cover everything, but you'll know where to start looking to stay on top of the new changes. I'll take this a namespace at a time.

System.Security

Managed strings are a bad storage medium for secrets. There's really no way to erase one. Garbage collection doesn't make any guarantees about overwriting collected memory, and in many cases, strings that become garbage remain in the managed heap for some time before being overwritten by a heap compaction. Moreover, the garbage collector might move them around in the managed heap. Combine that with the fact that it doesn't zero out old memory, and you may end up with many instances of the string you're trying to keep secret in your address space. A new class, SecureString, has been introduced to help alleviate these problems.

This class stores its data using the Data Protection API (DPAPI) protected memory model. In other words, data is always in its encrypted form while it is stored inside of a SecureString. The encryption key is managed by the local security authority subsystem (LSASS.EXE), and through the DPAPI, the data can be decrypted via interprocess communication.

In order for this class to be effective, any .NET Framework method that reads secret data (perhaps a password from the console, or a connection string from web.config) needs to directly encapsulate that data in a SecureString and return it to you. You can't simply wrap a SecureString around a normal string and expect it to be any safer—to be effective, the secret must never ever find its way into a normal managed string.

The same rule applies for giving secrets back to the .NET Framework. For example, the new Password property in ProcessStartInfo (I'll get to this a bit later) is of type SecureString. Under the covers, the .NET Framework will use the Marshal class to retrieve the actual contents of the secret data when it is needed via new functions, such as Marshal.SecureStringToBSTR. Because the Marshal class deals in unmanaged memory, that memory can be properly zeroed out when the Framework has finished using it. And no, you cannot use ToString to retrieve the secret from SecureString. Remember, you never want to end up with secret data in a normal string object.

One thing I don't see, however, is a way to keep the secret out of the swap file. When the secret data is unmarshaled into native memory, I see no way to ensure that the page to which it is unmarshaled is locked via VirtualLock.

The other thing I don't see is broad integration of SecureString into the framework, although the RTM bits do have a bit more than the early beta. One place you'll likely encounter this class is in the new overload for System.Diagnostics.Process.Start, which allows you to specify a user name and password. This uses the underlying Win32® CreateProcessWithLogonW function to spawn a process running in a new logon session with the user's credentials that you specify. You can specify the password with a SecureString. The System.Security.Cryptography namespace uses SecureString in three classes: CspParameters and the two certificate classes, X509Certificate and X509Certificate2. I also found SecureString being used in one of the MSBuild tasks. But what I'd really like to see is the following constructor:

public SqlConnection(SecureString connStr);

I'd also like to see a managed class that wraps Win32 functions like CredUIPromptForCredentials, so I can ask the user for a password and get back a SecureString as a result. Integration will be the key to the success of this feature.

Besides SecureString, there is another new class in this namespace that caught my eye: SecurityContext. Whoa is this ever a powerful class! It allows you to capture the security context of a thread and restore it on another thread. This includes the Code Access Security (CAS) thread markers, such as Assert and PermitOnly, and it also includes the impersonation token for the unmanaged thread.

There are some wild looking functions on this class, such as SuppressFlow, which controls whether CAS markers are flowed from one thread to another (they normally are). And then there was the shocker: SuppressFlowWindowsIdentity. Anybody who has been doing security programming in Windows® for long knows that when you kick off a new thread or perform an asynchronous call via a delegate, the impersonation token didn't used to flow to the new thread. This little security gotcha has bitten more than a few developers over the years. Well, if SecurityContext is offering a function to suppress the flow of a WindowsIdentity across threads, that must mean that the .NET Framework is now flowing impersonation tokens automatically.

To test this, I hacked up some code that called LogonUser and impersonated the resulting token. I then used a nifty new overload of the GetCurrent method on WindowsIdentity to determine if I was impersonating or not (more on this in a moment). I called this function from four different places: the main thread, a new thread kicked off via Thread.Start, a worker thread via an asynchronous delegate, and a thread kicked off by our good old friend from Win32, CreateThread. Oh, I even tried testing QueueUserWorkItem, which in the past has behaved a bit differently toward flowing security context (specifically, CAS markers). Here's the output:

Main thread Thread 2488 is impersonating V-CAMP-XP\alice Testing Thread.Start Thread 2508 is impersonating V-CAMP-XP\alice Testing QueueUserWorkItem Thread 2504 is impersonating V-CAMP-XP\alice Testing Delegate.BeginInvoke Thread 2516 is impersonating V-CAMP-XP\alice Testing Native CreateThread Thread 2520 is not impersonating!

Wow! So the runtime is indeed propagating impersonation tokens across threads automatically now, and if you want to change this behavior, you can use the SecurityContext class (though I don't recommend doing this).

Note that the thread created by the unmanaged API CreateThread isn't covered, but that's to be expected because it's the runtime, not the operating system, that's doing this work. This means that calls to inproc COM components that generate thread switches due to threading model mismatches will still drop the impersonation token just like before, but for a large class of applications, the cross-thread identity flow problem has been solved. This is great news, because it will lead to less surprising results for most people. This is an important and subtle change, so be prepared for it if you are using impersonation and asynchrony in your application.

System.Security.Principal

It may seem like a small thing, but the new class that models a security identifier (SID) paves the way for broad support of the Windows security programming interface. I'm going to be spending much less time writing managed C++ to bridge the gaps, and that makes me a very happy camper. There are three classes that make this possible. The first two are concrete classes that represent the two means by which you can reference a security account (user, group, or computer): SecurityIdentifier, which represents the machine-readable SID, and NTAccount, which stores the human readable string. The third, an abstract class called IdentityReference, binds these together.

A method that would normally return a SID, a collection of SIDs, or a collection of data structures that contain SIDs, will use the abstract class in its signature. The method will take an extra Type argument to allow you to choose whether you want the method to return SIDs or names. You can then safely downcast to the appropriate type. Here's an example that displays the account name of the user that owns a file:

void printTheOwnerOfThisFile(string path) { FileSecurity s = File.GetAccessControl(path); NTAccount user = (NTAccount)s.GetOwner(typeof(NTAccount)); Console.WriteLine(user); } ... printTheOwnerOfThisFile(@"c:\autoexec.bat");

Running that code on my system prints the following string:

BUILTIN\Administrators

There is also a complete listing of well-known SIDs in the new enumeration called WellKnownSidType. This makes it trivial to construct SIDs for well-known accounts without hardcoding strings like "Administrators", which isn't spelled the same in Germany as it is in the United States.

The WindowsIdentity class got some attention as well. As mentioned previously, one of my favorite additions is an overload of GetCurrent that allows you to distinguish between the thread and process security context during impersonation:

bool ifImpersonating = true; WindowsIdentity threadIdentity = WindowsIdentity.GetCurrent(ifImpersonating); if (null == threadIdentity) Console.WriteLine("Not impersonating"); else Console.WriteLine("Impersonating {0}", threadIdentity.Name);

System.Security.AccessControl

This is big. The Framework now models Windows security descriptors, allowing you to programmatically read and modify access control lists (ACL), take ownership of objects, convert between security descriptor description language (SDDL) strings and binary security descriptors, and more. I've watched the design of this namespace progress from the Alpha pre-release, and I must say, the work that went into it after the Alpha has made this a really powerful and easy-to-use addition.

You already saw a simple example of this new feature when I printed out the owner of a file. Notice how I was able to access the file's security descriptor via the System.IO.File class. You'll find that throughout the Framework, secure objects have sprouted two new methods: GetAccessControl and SetAccessControl. Classes like AutoResetEvent, Mutex, and the new Semaphore represent three such objects, as do the more obvious File and Directory classes.

There's no direct way of changing the discretionary access control list (DACL) for a service. There is simply no built-in managed representation of a service object right now. But after I learned how the access control infrastructure worked, it took me about 15 minutes to put together my own support for service security. The System.Security.AccessControl namespace was designed from the ground up with extensibility in mind. Kudos guys!

For anyone who has ever programmed ACLs before, you'll appreciate the simplicity of the code involved (see Figure 1). For more information on the System.Security.AccessControl namespace, see Mark Novak's article in the November 2004 issue of MSDN®Magazine, available at Safety in Windows: Manage Access to Windows Objects with ACLs and the .NET Framework.

Figure 1 Programming ACLs

FileSecurity sd = new FileSecurity(); // block inherited ACEs sd.SetAccessRuleProtection(true, false); FileSystemRights read = FileSystemRights.ReadPermissions | FileSystemRights.ReadData | FileSystemRights.ReadAttributes | FileSystemRights.ReadExtendedAttributes; sd.AddAccessRule(new FileSystemAccessRule( new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null), FileSystemRights.FullControl, AccessControlType.Allow)); sd.AddAccessRule(new FileSystemAccessRule( new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), read, AccessControlType.Allow)); File.SetAccessControl(@"c:\work\test.txt", sd);

System.Net.Security

One of my favorite topics for discussion is authentication protocols, but programming against them used to be rather complicated. Adding Kerberos to an application involved calling some low-level functions in the Security Support Provider Interface (SSPI), and adding Secure Sockets Layer (SSL) support was even more difficult.

Thankfully, there are now managed wrappers for SSPI that allow you to implement both client- and server-side secure channels using Kerberos or SSL. Kerberos support is brought to you by NegotiateStream, which technically negotiates between Kerberos and an older challenge-response protocol called NTLM. The client-side handshake can be either synchronous or asynchronous. Here's an example of synchronous setup for a client:

NegotiateStream secureChannel = new NegotiateStream(networkStream); secureChannel.AuthenticateAsClient( CredentialCache.DefaultNetworkCredentials, "SSPISample/TargetMachine:4242", ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Impersonation);

After doing that, you can use secureChannel just like you would a normal NetworkStream, but any data you push through secureChannel will be integrity-protected via a message authentication code (MAC) and encrypted. You'll need an instance of NegotiateStream on the server side as well in order to decrypt the incoming messages, and, of course, the server can obtain a WindowsIdentity representing the client via the RemoteIdentity property. From there the server can impersonate or perform role-based access checks.

SslStream looks very similar, but of course uses X.509 certificates as opposed to Kerberos tickets during authentication. It allows the client to check for server certificate revocation and provide a client certificate for mutual authentication. In the same way, once the authentication handshake is finished, you can push and pull data through the pipe and rest assured that it's being integrity-protected and encrypted.

System.Runtime.Remoting

Another exciting development is that the TCP channel now supports security. This is built into the channel using the NegotiateStream class I talked about earlier, and can be activated by specifying a few new attributes in the client and server remoting configuration files. I don't have room in this column to drill down into this topic, but there's a chapter covering this in my book, which you can read online at The .NET Developer's Guide to Windows Security.

System.Security.Cryptography

The DPAPI is now a first-class citizen in the .NET Framework, and two .NET classes, ProtectedData and ProtectedMemory, make it a breeze to use. You'll need to add a reference to System.Security.dll in order to use these classes:

private static byte[] Decrypt(byte[] ciphertext) { byte[] applicationEntropy = Encoding.ASCII.GetBytes("AcmeWidgets"); return ProtectedData.Unprotect(ciphertext, applicationEntropy, DataProtectionScope.LocalMachine); }

For those of you building cryptosystems using the .NET Framework, you'll be happy to know that there's an entirely new family of classes that derive from the new abstract class, keyed-hash message authentication code (HMAC). All the wide Secure hash Algorithm (SHA) variants are covered. Here's an example:

byte[] CalcMAC(byte[] key, byte[] msg) { return new HMACSHA256(key).ComputeHash(msg); }

If you are performing password-based cryptography, generating key material from passwords or pass phrases, you can now use the industry-standard algorithm PKCS #5 for generating a byte stream from a password. (The standard is documented in RFC 2898, which is available at Password-Based Cryptography Specification Version 2.0.) The new Rfc2898DeriveBytes class makes it easy to salt and stretch passwords, as I discussed in my October 2004 column (see Security Briefs: Password Minder Internals). Under the covers, this is using the PBKDF2 function with HMAC-SHA-1:

void CreateNewUserAccount(string name, string pwd) { Rfc2898DeriveBytes db = new Rfc2898DeriveBytes(pwd, 32, 1000); byte[] hash = db.GetBytes(32); addUserAccountToDatabase(name, db.Salt, hash); }

System.Security.Cryptography.X509Certificates and PKCS

No more CAPICOM! With these new namespaces, you've now got managed wrappers for the cryptographic API (CAPI) that allow you to manage certificates and create CMS/PKCS #7 enveloped or signed messages directly from any managed language.

Need to ask the user to choose a certificate from his personal store? It's pretty natural now:

X509Store store = new X509Store("My", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(store.Certificates, "MyTitle", "MyMessage", X509SelectionFlag.SingleSelection); if (1 == certs.Count) { Console.WriteLine("You selected \"{0}\"", certs[0].SubjectName.Name); } else Console.WriteLine("You canceled my dialog!");

Figure 2 shows the resulting dialog.

Figure 2** Choosing a Certificate **

System.Security.Cryptography.Xml

This namespace has been fleshed out with support for XML Encryption. For information on using XML Signature and XML Encryption to exchange data securely, see Mike Downen and Shawn Farkas's article in the November 2004 issue of MSDN Magazine at Trustworthy Code: Exchange Data More Securely with XML Signatures and Encryption.

System.Diagnostics

Besides a whole boatload of new tracing and debugging features, better event log support, and even a Stopwatch, there's a hidden gem lurking inside of the Process class: the ability to invoke CreateProcessWithLogonW without having to explicitly use P/Invoke. This is the "run as" feature in Windows that developers use all the time to run programs using alternate credentials when testing. Now it's easy to use programmatically from managed code:

void RunNotepadAsThisUser(string authority, string principal, SecureString password) { Process.Start(@"c:\windows\notepad.exe", principal, password, authority).Dispose(); }

Note the use of SecureString—that's the only trick here because where do you get one of those? One approach would be to use one of the Win32 credential APIs, such as CredUIPromptForCredentials, to ask the user for a password, and then you could stuff the unmanaged string into a SecureString instance that you can then pass to the Process.Start method. After that you would carefully zero out the buffer that held the password.

System.DirectoryServices

The biggest change here seems to be the addition of support for the Virtual List View (VLV). This Windows Server™ 2003 feature allows you to perform very large searches and scroll through them efficiently. You don't have to retrieve the entire resultset at once. With the VLV, you can specify a window of results that you want, and then scroll the window. Here's an example that searches for all users in the forest, but only returns the first 25 records:

DirectorySearcher search = new DirectorySearcher( "(&(objectClass=user)" + "(objectCategory=person))"); search.VirtualListView = new DirectoryVirtualListView(25); foreach (SearchResult result in search.FindAll()) { Console.WriteLine(result.Path); }

Once you create a VLV and attach it to a searcher, you can control the window of displayed results by setting properties like Offset (which is the index into the resultset by record number), TargetPercentage (the index into the resultset by a percentage of the number of records), or Target (the index into the resultset via a string that the desired result should start with).

System.DirectoryServices.ActiveDirectory

This is a new namespace that makes it easy to view and manipulate the physical and logical configuration of Active Directory®. Here are some of the class names to give you an idea of what's modeled: Forest, Site, Domain, DomainController, GlobalCatalog, and there are many more. Also provided here is a set of classes for reading and manipulating the Active Directory schema. I could spend an entire column talking just about this namespace—there's lots of great stuff here, so be sure to check it out. For an in-depth discussion consider reading The .NET Developers Guide to Directory Services Programming, by Joe Kaplan and Ryan Dunn (Addison-Wesley Professional, May 2006).

System.Web

Back in June 2004, I wrote an article on the new security features in ASP.NET 2.0 (see Security: Security Headaches? Take ASP.NET 2.0!). But there's one thing that I didn't cover, and that's configuration file encryption. Wouldn't it be nice to just ask the .NET Framework to use DPAPI to encrypt an entire section of your configuration file, perhaps the AppSettings section? With the following command line, you can do just that:

aspnet_regiis -prov DataProtectionConfigurationProvider -pef appSettings c:\web

This turns a web.config file that started life looking like this,

<configuration> <appSettings> <add key='MySecret' value='Attack at dawn'/> </appSettings> </configuration>

into something that looks like this (I've abbreviated the ciphertext to keep things readable):

<configuration> <appSettings configProtectionProvider="DataProtectionConfigurationProvider"> <EncryptedData> <CipherData> <CipherValue>AQAAANCMnd...</CipherValue> </CipherData> </EncryptedData> </appSettings> </configuration>

Now guess how you access the "MySecret" data. That's the cool part—you access it just like you would have before, and the decryption happens behind the scenes:

Console.WriteLine(ConfigurationSettings.AppSettings["MySecret"]);

There's also an RSA provider you can use that relies on a CAPI key container to store the encryption key. You would use this class if you needed to deploy a single config file to many different machines in a Web farm, for example. With CAPI, you can synchronize the encryption key across the entire farm, whereas DPAPI is a machine-specific encryption technique. The patterns & practices team has put together a set of documents that cover how to use this and other security features in version 2.0 (available at Security Guidance for Applications Index).

Changes in Code Access Security

A lot has been added to the CAS infrastructure for version 2.0 of the .NET Framework. A new concept of transparent versus opaque assemblies can help make your security reviews easier for partial trust scenarios. Shawn Farkas has an excellent description of these ideas and how to put them to use in his blog: Marking Your Code Transparent.

A new class of evidence tells you whether an assembly was loaded from the global assembly cache (GAC). Note that there was a significant change to security policy regarding the GAC. In version 2.0, all assemblies loaded from the GAC are implicitly granted FullTrust, regardless of security policy. This was a performance optimization that was possible because very few people were running GACed assemblies under partial trust. But if you were one of those people, don't be caught off guard by this change!

There are several permissions that moderate access to the new features in the .NET Framework 2.0. Take, for example, SmtpPermission which regulates access to the client-side SMTP functionality exposed by the new System.Net.Mail namespace.

One new permission that I think is pretty neat is the DataProtectionPermission, which controls which code is allowed to use the DPAPI classes ProtectedData and ProtectedMemory. This is really important because you don't want random code that was downloaded from some untrusted source to be allowed to decrypt that config file that we just got so excited about encrypting.

Wrapping It Up

Version 2.0 of the .NET Framework is a boon for developers who care about security. I've only scratched the surface of the work that this team has done—it's at least as large an effort as what we've seen happen in ASP.NET 2.0, as far as I can see.

Send your questions and comments for Keith to  briefs@microsoft.com.