Developer
Featured Article |
Keeping Confidential
Information Secure |
 |
By Scott Mauvais, MCSE, MCSD,
MCDBA
Most applications, no matter how trivial,
have some sensitive information they need to protect. Obvious
examples of sensitive information include user credentials and
personal data such as medical and financial records. Even if
your application does not consume or store sensitive
information, you probably need to protect your application's
configuration data such as license keys or file locations lest
your application be vulnerable to piracy or denial of services
attacks.
Storing confidential information is one of the
more vexing problems we developers face. On the one hand, our
application needs to access this data, yet we need to prevent
unauthorized access to it. The inherent problem is that given
sufficient privilege, an attacker can impersonate the
application and thus gain access to the protected data. In
this article, I will cover some techniques that you can use to
write more secure, robust applications by safely storing
secrets in your applications. Although I will focus mainly on
applications built for the Microsoft� .NET Framework, most of
the techniques are relevant to classing Win32� programs also.
|
Storing secrets is just a small
part of building secure applications. For a more thorough
discussion of the topic, you should pick up a copy of Michael
Howard and David LeBlanc's new book, Writing
Secure Code. The book covers everything from preventing
buffer overruns to controlling access to testing security.
They even have an entire chapter devoted to storing secrets.
To get a feel for the book, you can review the table
of contents and read a Chapter
12: Securing Web-Based Services on the Microsoft Press�
site. To dive into even more detail, you will want to read
Michael Howard's previous book, Designing
Secure Web-Based Applications for Microsoft Windows� 2000,
for which you'll also find a table
of contents and Chapter
5: Internet Information Services Security Overview at the
Microsoft Press site. Michael is a program manager on the
Windows 2000 security team, so he provides unique insight into
making the most out of the Windows 2000 security model. David
is a senior security technologist in Microsoft's Information
Technology Group, where he focuses on defending the Microsoft
network from attack.
|
Just Don't
Do it |
Relax, the best solution to storing secrets is the easiest
one: Don't store them in the first place. Storing sensitive
data is risky, and you should treat secrets just like you do
any other risk. The first technique in risk management is to
transfer the risk to somebody else. Rather than spending
resources trying to figure out how to protect confidential
information, you are better off trying to figure out how not
to store it in the first place.
This approach has a
number of benefits. First off, it reduces the attack surface:
the fewer places information is stored, the less likely it is
to leak out. Second, it reduces the resources you need to
spend developing your application and thus allows you to
either ship sooner or add in more features�either way, you
win. Third, when you centralize the storage of sensitive
information, you can focus resources on protecting that one
instance and auditing its usage.
Sure, the benefits
sound great, but how exactly does one go about transferring
the risk to someone else? As an example, let's look at what is
probably the most common secret that applications store:
passwords. If you are in a Microsoft Windows environment,
there are very, very few cases in which you should be managing
passwords in your applications. Rather, you should rely on
Microsoft Windows Authentication. Besides the benefits
transferring risk that I mentioned earlier, using Windows
Authentication lets you leverage all the great features of
Microsoft Windows password management such as aging, minimum
complexity, and account lockouts. To learn more about
Microsoft Windows Authentication, see the Security Support
Provider Interface section of Jeffrey Richter's Programming
Server-Side Applications for Microsoft Windows
2000.
If you can't rely on Microsoft Windows
password management for some reason�for example, if you need
to pass credentials through a firewall and the security policy
forbids opening up the required ports�another approach is to
store a verifier rather than the password itself. Under this
approach, you store something you can use to verify that the
user knows the password. A common approach is to hash the
password and then store the hash rather than the password
itself. When a user enters his or her password, you hash it
and compare the output of your hashing algorithm to the hash
you stored. If they match, then you can be sure the user knows
the password. The benefit of this approach is that even if
your application is compromised, all the attacker gets are the
hashes, rather than the passwords themselves. For more
information on this technique, see the "Storing Secrets"
chapter in Writing
Secure Code, by Michael Howard and David Le Blanc's.
Incidentally, this is how Microsoft Windows Authentication
works: a user's password is never placed on the wire; rather,
only a hash is sent across the network. |
|
Make It
Hard |
Unfortunately, you can't always take the easy way out, and
there are times when you will have to store secrets. If
transferring the risk is not feasible, the next best strategy
is to minimize the likelihood that a loss will occur. For
example, maybe you need to access a back-end system that is
not (gasp!) running on Microsoft Windows, or it is your
application that will be the system of record for some
sensitive information, and you want to encrypt that data.
While these examples may seem fundamentally different, the
approach is the same: you want to reduce the probability of
exposing the sensitive information by encrypting it. (You
weren't going to store that non-Windows password in clear text
were you?) This should be pretty easy. All you need to do is
pick a strong encryption library, encrypt the data, and then
save it to your favorite persistent store. If you are feeling
particularly dutiful, you might even add an extra level of
protection and use an access control list (ACL) to protect the
ciphertext so that only authorized users can access it. Simple
enough, right?
Well, not quite. Can you spot the flaw in this reasoning?
It is a common belief that encryption is the solution to the
problem of storing secrets. Actually, it's only half of the
solution.
The part that is missing is you still need to protect the
encryption key. If an attacker gains access to your key, all
those CPU cycles you spent encrypting that data are for naught
because he or she can just use that key to decrypt it. As I
mentioned in my introduction, the fundamental problem here is
that a privileged account can access any part of the system,
so no matter where you store an encryption key, it is
vulnerable to a rogue administrator or an attacker that has so
compromised the system that she or he has gained enough
privilege to access it. One way to make it more difficult
for an attacker is to use the Data Protection API (DPAPI)
included in Microsoft Windows 2000 and later.
|
Data
Protection API |
Let's look at some of the advantages of using the DPAPI
over some other encryption techniques. First, it's part of the
underlying operating system, so this means you can count on
DPAPI being installed on every (Microsoft Windows 2000 and
later) machine. Better yet, it supports the concept of
transferring the risk because you don't need to worry about
it. The second advantage is that you don't have to worry about
storing the encryption key. Describing exactly how the
encryption key is generated and stored is out of the scope of
this article but at a very high level, the DPAPI generates the
encryption key based on a preexisting secret�the user's
password. While this is an over-simplification of the process,
it will work for the purpose of this article. To learn how the
process really works, you can find an excellent discussion on
the topic in the Windows
Data Protection white paper available on MSDN�.
Using the DPAPI is pretty easy because it contains only two
methods: CryptProtectData and CryptUnprotectData. To encrypt data, pass the
clear text into CryptProtectData, and it will return the
ciphertext. To decrypt, simply reverse the process by passing
the ciphertext to CryptUnprotectData, and it returns the clear
text. Because the DPAPI leverages the user's credentials when
generating the key, users logged on with different accounts
will receive different output, so you don't need to worry
about protecting data from other users on the same system.
If you are familiar with using the Local Security
Authority's (LSA) LsaStorePrivateData and LsaRetrievePrivateData, there are some important
differences you need to be aware of. First, DPAPI does not
actually store or retrieve the secret for you. Rather it
simply encrypts and decrypts; storage is up to you. Overall,
this is a good thing because now you can store the secrets
using whatever means is most appropriate for your
application�you just need to remember to do it because the API
does not do it automatically. Second, while the LSA has a
limited number of secrets it can store (4096, and half of
these are reserved for the operating system), there is no such
limit on the DPAPI. Finally, a user does not need
administrative privileges to call the DPAPI, so you can adhere
to the least-privilege principle when designing your
application. |
|
DPAPI and
the Microsoft .NET Framework |
The DPAPI is part
of the Cryptography API (Crypto API) and is implemented in
crypt32.dll, which is native code. If you want to use it from
managed code such as Microsoft Visual Basic� .NET or Visual
C#�, you will need to use P/Invoke. The resource CD that is
included in Howard and LeBlanc's Writing
Secure Code contains some sample code that wraps the DPAPI
in managed code.
If you want to use
the DPAPI from an ASP.NET application, you need to take a
couple of other things into consideration. Because the DPAPI
relies on an account password to create the encryption key, it
needs to load a user profile. Unfortunately, the ASPNET
account does not have a user profile. (Astute readers may want
to try having the Web application impersonate the caller, but
this will not work either. This will give the thread an
impersonation token that is a network logon session, and
network sessions do not load the user's profile.) Rather
than using a user account for the encryption, the DPAPI
supports using the machine account by passing the CRYPTPROTECT LOCAL
MACHINE flag when you call the DPAPI
methods. The drawback here is that the secret is no
longer tied to the user; it is tied to the machine. This means
that any process running on the machine�say an app placed
there by the attacker�is able to decrypt your secret. Of
course, you could ACL the file or registry key where you
stored the encrypted text to prevent unauthorized access to
it. However, a user with sufficient privilege can easily reset
your ACLs.
A better approach
is to use an Enterprise Services (COM+) application and have
it call the DPAPI. Using this approach, first create an
Enterprise Services application that wraps the DPAPI calls and
install it with a fixed identity that will give it access to a
user profile. At runtime, your Microsoft ASP.NET application
retrieves the encrypted string and passes this string to a
method on your Serviced Component. The component then
P/Invokes the DPAPI, passes it the cipher text, and returns
the clear text to the Web application. Diving farther into the
details of this approach is outside the scope of this article,
but this should provide enough information to get you started.
Tim Ewald's article, COM+
Integration: How .NET Enterprise Services Can Help You Build
Distributed Applications, on MSDN provides some great
background on enterprise
services.
|
For More
Information |
If you remember nothing else from this article, remember
this: try not to store secrets in the first place. When you
are presented with a secret you think you need to store, the
first thing you should do is reexamine your application's
design and verify that you really do need to store it. If you
do need to store it, use Microsoft Window's built-in
encryption functions such as the DPAPI rather than trying to
implement your own. Finally, remember that you need to do your
best to protect yourself against not only outside attackers
but also rogue administrators, so you will want to use a
defense in-depth approach.
As I mentioned at the
beginning of the article, the best place to learn about
developing secure applications is to read Michael Howard and
David LeBlanc's new book, Writing
Secure Code. You might also want to check out the
following Microsoft Press resources, which provide in-depth
documentation for all issues related to developing secure
applications:
Microsoft Press provides in-depth documentation for these
and all the other issues related to developing for .NET. For a
complete list of .NET titles from Microsoft Press, see the Inside
Information About Microsoft .NET
page. |
 |
 |
|
Last Updated: Monday, September 30,
2002 |
| |
 |
 |
|