As a developer I spend a lot of time working on application security, and a question I often get asked is what's the best way for a program to protect secrets such as passwords or cryptographic keys. First of all, I should say that storing secrets completely securely in software is impossible. Having said that, however, developers should not give up and throw in the towel. In this article, we will look at various techniques to protect your secrets from the vast majority of attacks.
The most secure way to protect sensitive data is not to store it. If an application needs to work with a secret such as a password or cryptographic key, you should consider whether your application really need to store this secret. Below we look at two alternatives to storing secrets.
If a user needs to provide a password for the purpose of authentication, there is no need for the application to store a copy of the password. Instead the application stores what is called a hash of the password. A hash is a "one-way function" that is mathematically next to impossible to reverse. In other words, there should no way to determine the password from the hash value other than by guessing (i.e., brute force attack).
The steps to implement password hashing are straight forward:
By not storing the actual password, even if an attacker manages to get access to your machine, they won't be able to unhash the hash value to get the password. Password hashing is a widely used security technique for dealing with password based authentication. In fact, the technique is used by both the Unix and Windows operating systems.
If an application uses a cryptographic key, an alternative to storing the key is to derive it from a user supplied password. Windows provides a number of methods for turning user supplied passwords into cryptographic keys including:
A common mistake made by developers is to hide sensitive data in source code in the belief that secrets compiled into a binary are safe from prying eyes. This could not be further from the truth.The effort required for a reasonably skilled attacker to find an encryption key or any other secret embedded in a binary is minimal. Attackers have a range of tools at their disposal, including disassemblers and debuggers that will allow them to quickly find any secrets buried in your code.
A common place that developers store sensitive information is in some obscure file or registry entry. This is done in the false belief that it would be difficult for an attacker to find data in such an unusual place. Again, the amount of effort for attackers to find secrets hidden in this way is minimal. Using tools such as Filemon and Regmon from Sysinternals an attacker can quickly determine the files and registry entries an application is reading.
If your application must store secrets then they should be encrypted. Under Windows 2K and later, a good approach is to use the Data Protection API (DPAPI). The main advantage of DPAPI is that you can use it to encrypt your secrets, but do not need to manage yet another key, instead you offload the responsibility of key management to the operating system.
DPAPI comprises a pair of functions (CryptProtectData & CryptUnprotectData) that provide easy to use OS-level data protection services to the user and system processes. DPAPI can generate either user specific or machine specific cryptographic keys. These two types of key are normally referred to as user store and machine store.
The focus of DPAPI is to provide user specific keys. That is the data is protected so that only the user that encrypted the data is able to decrypt it. To do this DPAPI derives a key using PKCS#5 from the user's credentials, and stores this key in the user's profile.
If your program is running under an account that doesn't load a user profile, such as ASP.NET, then you will need to use the machine store. This isn't as secure as the user key option as any process running on the machine is able to decrypt information encrypted with the machine key. However, DPAPI provides a way to mitigate this problem by allowing the caller to add something called secondary entropy which is really an additional secret. This secondary entropy is added when encrypting the data and is also required to decrypt it. Applications running under other accounts should not have access to this secondary entropy and will therefore not be able to decrypt the information even though it was encrypted with the machine level key.
To prevent applications running under other accounts from accessing the secondary entropy, it is important this secondary entropy is protected with a good ACL (windows access control). The actual encrypted data should also be protected with a good ACL to restrict access to a specific user account.
Unfortunately, if you are using .NET 1.1 or earlier then you will need to create a managed code wrapper around DPAPI. If you are using .NET 2.0, you are in luck as DPAPI can now be accessed from managed code using the new ProtectData class.
Avoid storing secrets if at all possible. Consider the alternatives; for example, storing the hash of the secret or deriving the secret from a user supplied password. Do not rely on security through obscurity. Techniques such as hiding secrets in files do not provide real security. If you must store sensitive data then you should encrypt it and restrict access through good access control.
We welcome your feedback and/or corrections to this article.
Phil Ratcliffe: [email protected]
Last modified: 24th Oct 2012