[Editor's Update - 1/20/2006: This article refers to a beta version of Visual Studio 2005. An updated version of the article, reflecting features found in the final release of Visual Studio 2005, can be found at Security Briefs: Security Enhancements in the .NET Framework 2.0.]

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

As I write this column, version 2.0 of the Microsoft® .NET Framework is at Beta 1. When I got my 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 as well as 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 month I'm 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. Oh, and the obvious caveat applies: this is beta software, so anything I talk about here could change before the final release.

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, perhaps 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 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 in Beta 1 is broad integration of SecureString into the Framework. So far the only place I see it being used is ProcessStartInfo. Until this class is integrated more fully into the Framework, its real power won't be realized.

For example, I'd like to see 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. There's even some documentation for this class in Beta 1, and 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 doesn't 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 (either a 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 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 has always been just a little bit tricky. Adding Kerberos to an application used to involve 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.ClientAuthenticate( 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 his 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

DPAPI is now a first-class citizen in the .NET Framework, and two .NETclasses, ProtectedData and ProtectedMemory, make it a breeze to use:

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 SHA variants are covered. Here's an example:

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

If you're doing password-based cryptography, generating key material from passwords or pass phrases, you can now use the industry-standard algorithm for generating a byte stream from a password, PKCS#5 (documented in RFC 2898). The new Rfc2898DeriveBytes makes it easy to salt and stretch passwords, as I discussed in my last column. Under the covers, it's using the PBKDF2 function with HMAC-SHA-1:

void CreateNewUserAccount(string name, string pwd) { Rfc2898DeriveBytes db = new Rfc2898DeriveBytes(password, 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 CryptoAPI (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 her personal store? It's pretty natural now:

X509Store store = new X509Store("My", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509CertificateExCollection certs = store.Certificates.Select( "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. If you were paying attention to that code, you may have noticed that the .NET Framework team has gone down the slippery slope of naming a class with an "Ex" suffix. Wasn't it Mr. Bunny's Guide to ActiveX® controls that taught us not to name stuff this way in COM? Just wait, I'll bet in a few years you'll see an Ex2 version.

Figure 2 Choosing a Certificate

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 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 way would be to use one of the Win32 credential APIs such as CredUIPromptForCredentials to ask the user for a password, and then stuff the unmanaged string into a SecureString instance that you can pass to the Process.Start method. Then you would carefully zero out the buffer that held the password. I'm looking forward to having the credentials API wrapped in the .NET Framework. Maybe we'll get lucky and see it emerge in the next Beta.

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.

System.Web

Back in June 2004, I wrote an article on the new security features in ASP.NET 2.0, so I won't repeat all that stuff here (see Security Headaches? Take ASP.NET 2.0!). But there's one thing that I didn't cover that appears to be fully baked now, 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? Well, check this out:

// use this app's config file string configFilePath = null; Configuration config = Configuration.GetExeConfiguration(path, ConfigurationUserLevel.None); config.AppSettings.ProtectSection(ProtectedConfiguration. DataProtectionProviderName); config.Update();

This turns a config file that started life looking like this

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

into something that looks like Figure 3 (I've abbreviated the ciphertext to keep things readable).

Figure 3 The Encrypted Text

<configuration> <protectedData> <protectedDataSections> <add name="appSettings" provider="DataProtectionConfigurationProvider" /> </protectedDataSections> </protectedData> <appSettings> <EncryptedData> <CipherData> <CipherValue>AQAAA...</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.

Changes in Code Access Security

A whole bunch of stuff has been added to the CAS infrastructure for version 2.0 of the .NET Framework. New extensibility points in the stack-walking mechanism allow permissions to override the handling of demands (look up CodeAccessSecurity.CheckDemand and friends if you're interested in learning more).

A new class of evidence tells you whether an assembly was loaded from the global assembly cache (GAC).

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.

Besides all this, there's a whole new set of classes to deal with ClickOnce deployment and trust determination, but I'll save that for another column.

Wrapping it Up

Version 2.0 of the .NET Framework is a boon for developers who care about security. I've just scratched the surface of the work that this team has done—it's at least as large an effort as what we've seen happening in ASP.NET 2.0, as far as I can see. This is definitely a version I'm looking forward to working with. Have fun experimenting with the Beta!

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

Keith Brown is a co-founder of Pluralsight, specializing in developing and delivering high-quality training for software developers. Keith's most recent book, The .NET Developer's Guide to Windows Security, is available here.