In my day job I am required to store login credentials to numerous other systems on the users local system. A feature of the product is that the credential data is not just stored locally, it is also synchronized up to a master database that is shared by many users. Since I need to be able to replay the passwords on the local system I cannot just store hashes of the passwords.
I try to be a security conscious developer so I know that I need to use a strong encryption algorithm and that I cannot use a common encryption key nor should the key be easy to derive from other information available to a malicious user who has access to the master server. The key needs to be both strong and unique per user and I need as the encryption/decryption to be transparent to the end user. As the password only needs to be decrypted on the local workstation I decided to use the Data Protection API (DPAPI) feature available in Windows to encrypt and decrypt the passwords I am storing. With DPAPI I can encrypt data using the unique encryption key that is stored and automatically managed on the users workstation, and I can also take advantage of the ability to add extra entropy to the encryption each time the password value is changed so that it is more difficult to analyze the encrypted values to try to determine the encryption key.
I am using POCO Data Transfer Object (DTO) types that I pass on to my repository for persistence as well as using the DTO for my View Model. By encrypting the values passed in to the property setter I ensure that whenever I read the data back out of the property I am reading the encrypted version of the value. This way my local persistence will save the encrypted value and synchronize it up to the server where no one, not even any of the server administrators, should be able to decrypt it.
The actual implementation of encrypting the values with DPAPI is not difficult but it is repetitive, boilerplate code and using it means that I cannot have nice clean DTO definitions since I can’t use auto-implemented properties. I also want to give myself a way to get the cleartext data back out of a property in the event that I may need it for some reason. Adding that type of functionality requires even more repetitive code.
Seeing that there is going to be a bunch of copy/paste coding in my future I turn to my toolbox and decide to take advantage of Aspect Oriented Programming (AOP) to refactor this cross cutting concern into some aspects that I can easily apply to the DTOs. In these examples I am using PostSharp to accomplish the AOP functionality other .NET AOP tools work similarly. By using AOP and just decorating the appropriate DTO properties with the aspect attributes I ensure that the required logic is injected into the assembly for me. I also get to continue to use my nice clean auto-implemented property definitions so my source code is clear and uncluttered.
First, lets take a look at how I want my one of my DTOs to look:
Using the attributes (that I will go into detail on below) I declare that this type is going to contain at least one automatically encrypted property via the [EncryptedType] attribute. I also declare that the Password property is going to automatically encrypt values passed to its setter by decorating it with the [Encrypted] attribute.
Since any encrypted property will return the encrypted data from its getter I want to give myself an explicit override for when I really do need to retrieve the cleartext value stored in the property. This is where my IEncrypted interface comes into play.
Types that implement the IEncrypted interface will provide a method named “AsClear” that return the decrypted value of the property name passed into it. This way I can explicitly retrieve the cleartext of an encrypted property but I don’t run the risk of getting it by default.
Changes to the containing type ([EncryptedType])
First, we need to enforce ordering of the aspect application. This is because the dictionary that is used to hold the property values needs to be injected in the type definition before the implementation code that references that dictionary is injected into the properties. The [AspectTypeDependency] attribute ensures that the [EncryptedType] transformation will take place before the [Encrypted] transformation of any properties.
I am using a dictionary to store the encrypted property values so that I have access to the values from both the AsClear method and the individual property setters and getters. I am accomplishing that by instructing PostSharp to add a new property to the class for accessing that dictionary (in the body of the aspect). Later we will pull a reference to that dictionary into the property itself.
Next, I add the IEncrypted interface to the type. This is accomplished with the [IntroduceInterface] attribute. Now I don’t need to worry about cluttering up my type definitions with interfaces that they may or may not use. It is only when I define a type to have encrypted properties that it will have this interface implementation added to it after compilation.
I ensure that this attribute can only decorate class definitions, since that is the only place it makes sense.
I then I use the [IntroduceMember] feature of PostSharp to add the dictionary of encrypted property values to the type. These values will be stored in the EncryptedValues property. This demonstrates how you can use AOP tools to not just modify method execution but add entirely new behaviors.
I add the RuntimeInitialize method so that when a new instance of my type is created the EnctyptedValues dictionary is initialized and then I implement the AsClear method of IEncrypted. The AsClear method will return the cleartext value of the given property by decrypting the encrypted value and returning it.
The method of storing the additional entropy data used to encrypt the value prepended to the encrypted value is a bit naïve but it works for my situation. By doing this I ensure that even if I encrypt the same value with DPAPI multiple times the additional entropy will ensure the resulting cyphertext is unique. This is helpful because even though we keep telling users not to reuse passwords across sites we know that they still do. With the additional random entropy we make it more difficult for someone to determine if a given user reuses a common password.
Changes to the properties ([Encrypted])
The encrypted property attribute first ensures that properties decorated with it will be processed by PostSharp after their containing types. This is essential so that the [ImportMember(“EncryptedValue”)] code will be able to pull in a reference to the EncryptedValue property of the type so that the setter and the getter interceptors can use that dictionary to access the stored encrypted values. I also ensure that the [Encrpted] attribute can only be applied to properties or fields.
In the CompileTimeInitialize method I have an optimization to get the name of the property (or field) to be used as the Dictionary key. Then I go ahead and import a reference to the EncryptedValues property into EncryptedValueStore using the [ImportMember] feature of Postsharp.
The meat of the implementation is in the OnSetValue method, which is invoked when the property or field is set. In there I take the value that was passed in, generate some additional random entropy values and encrypt it with DPAPI. Then I prepend a Base64 string containing the additional entropy to a Base64 string of the encrypted value and update the EncryptedValue dictionary with the encrypted value.
The OnGetValue method is executed when the property or field is read. If the EncryptedValue dictionary contains an appropriate value then the encrypted value string is returned to the caller.
With this code we have created a way for us to ensure that sensitive data is encrypted so that only the user who encrypted it can decrypt it. The default behavior is that no matter how you read the property you will only read the encrypted representation of the data. In order to actually get the cleartext out you are going to have to jump through the hoops of casting to IEncrypted and knowing the name of the property you need the value of. In this way we ensure that our fellow developers easily fall into the pit of success and only persist correctly encrypted values into our data stores.
There are two major caveats to keep in mind if you implement this solution. The first is that the storage of the additional entropy prepended to the encrypted data is not an ideal situation. While you should always use random additional entropy values when using DPAPI (and always use different values each time you encrypt data) it is better to store that additional entropy value somewhere other than attached right to the encrypted data.
The second thing to keep in mind is that this is encryption based on secrets stored on the local system (or in Active Directory if applicable). This means that if the user reinstalls Windows they will lose the ability to decrypt data that was previously stored. There may also be other situations where the data becomes unreadable.
All of the source code used is available in my BitBucket repository under an MIT license with the usual disclaimers that this is demonstration code and may not be suitable for production use.
I welcome your feedback, either on this blog or on Twitter. Please let me know what you think and if there are any specific topics or issues you would like me to cover.
- Finding Bad Controllers With AOP
- Fall 2012–Crypto, AOP and AppSec