January 2010
Volume 25 Number 01
Cloud Security - Crypto Services and Data Security in Microsoft Azure
By Jonathan Wiggs | January 2010
Many early adopters of Microsoft Azure still have a lot of questions about platform security and its support of cryptography. My hope here is to introduce some of the basic concepts of cryptography and related security within Azure. The details of this topic could fill whole books, so I am only intending to demonstrate and review some of the cryptography services and providers in Azure. There are also some security implications for any transition to Azure.
As with any new platform or service delivery method, you’ll be faced with new challenges. You’ll also be reminded that some of the classic problems still exist and even that some of the same solutions you’ve used in the past will still work very well. Any application engineer or designer should think about this topic as it relates to the kind of data you may be storing as well as what you need to persist. Combine this into a methodical approach and you and your customers will be well-served.
So why would I think this information is needed within the developer community? Over the last several months I’ve seen an increasing number of posts on the community sites regarding security in general with Azure. Microsoft has suggested encryption as part of securing application-layer data with Azure projects. However, proper understanding of both encryption and the .NET security model will be needed by product designers and developers building on Azure.
One thing I noticed was an increasing percentage of posts specific to crypto services and key storage. This was especially true with regards to Azure Storage services. It got my own curiosity going, and I discovered it was a worthy topic to discuss in some depth.
During the course of this article, I’ll be making heavy use of Cryptographic Service Providers (CSPs), which are implementations of cryptographic standards, algorithms and functions presented in a system program interface. For the purposes of this article I’ll be using the symmetric encryption algorithm provided by the Rijndael cryptography class.
Crypto Basics
The Microsoft Azure SDK extends the core .NET libraries to allow the developer to integrate and make use of the services provided by Azure. Access to the CSPs has not been restricted within Azure projects and services. This means much of your development with regard to encrypting and decrypting data will remain the same with regards to the assemblies you’re accustomed to using. However, there are changes in the underlying architecture, issues of when or where to encrypt data and where and how to persist keys. I’ll discuss key and secret data persistence a little later in this article.
You also have access to the full array of cryptographic hash functionality in Azure, such as MD5 and SHA. These are vital to enhance the security of any system for things such as detecting duplicate data, hash table indexes, message signatures and password verification.
A consistent recommendation is to never create your own or use a proprietary encryption algorithm. The algorithms provided in the .NET CSPs are proven, tested and have many years of exposure to back them up. Using XOR to create your own cipher process is not the same, and does not provide the same level of data security.
A second recommendation is to use the RNGCryptoServiceProvider class to generate random numbers. This ensures random numbers generated by your application will always have a very high level of entropy, making it hard to guess at the patterns.
The code below implements a single static member that returns a 32-bit int value that is random and meets the requirements to be cryptographically secure. This is made possible by using the byte generator in the RNGCryptoServiceProvider found in the Cryptography namespace:
public static int GenerateRandomNumber() {
byte[] GeneratedBytes = new byte[4];
RNGCryptoServiceProvider CSP = new RNGCryptoServiceProvider();
CSP.GetBytes(GeneratedBytes);
return BitConverter.ToInt32(GeneratedBytes, 0);
}
Figure 1 shows a simple example of using the CSPs within Azure. Three public members are exposed for use within any Azure application. The first accepts a binary key and initialization vector (IV), as well as a binary buffer of unencrypted data and returns its encrypted equivalent. The second member does the reverse by decrypting the data. The third member returns the calculated hash value for that data. Notice here that I’m using the Rijndael CSP for managed access to a provider. I’m also storing data and keys in binary buffers and writing over them as soon as I’m finished with them. I’ll touch on this topic later when I discuss immutability.
Figure 1 Simple Encryption
public static byte[] SampleEncrypt(byte[] dataBuffer,
byte[] Key, byte[] IV) {
MemoryStream InMemory = new MemoryStream();
Rijndael SymetricAlgorithm = Rijndael.Create();
SymetricAlgorithm.Key = Key;
SymetricAlgorithm.IV = IV;
CryptoStream EncryptionStream = new CryptoStream(InMemory,
SymetricAlgorithm.CreateEncryptor(), CryptoStreamMode.Write);
EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
EncryptionStream.Close();
byte[] ReturnBuffer = InMemory.ToArray();
return ReturnBuffer;
}
This is the simplest example of encrypting data and returning the encrypted results as a byte array. This is not code that should be used in a secure environment without all the proper security analysis, only an example.
The example in Figure 2 has an almost identical structure to the one in Figure 1. In this case, I’m decrypting data based on the same key and IV, only with an encrypted byte buffer as a parameter. The only real difference here is that when I create the encryption stream, I specify that I’m creating a symmetric decryptor and not an encryptor as I did previously.
Figure 2 Simple Decryption
public static byte[] SampleDecrypt(byte[] dataBuffer,
byte[] Key, byte[] IV) {
MemoryStream InMemory = new MemoryStream();
Rijndael SymetricAlgorithm = Rijndael.Create();
SymetricAlgorithm.Key = Key;
SymetricAlgorithm.IV = IV;
CryptoStream EncryptionStream = new CryptoStream(InMemory,
SymetricAlgorithm.CreateDecryptor(), CryptoStreamMode.Write);
EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
EncryptionStream.Close();
byte[] ReturnBuffer = InMemory.ToArray();
return ReturnBuffer;
}
Key Storage and Persistence
As with any encryption strategy at the application or enterprise layer, the encryption and decryption infrastructure is less than half the battle. The real problem comes with key storage and key persistence. The data security provided by encrypting data is only as secure as the keys used, and this problem is much more difficult than people may think at first. Systems I’ve reviewed have stored crypto keys everywhere, from directly in source code, to text files named something clever, to flat files stored in hard-to-find directories.
An important question of key persistence comes about when considering where to store and keep keys in a cloud environment. Some people have expressed concern that by persisting keys in the cloud you’re exposing yourself to a security threat from the cloud itself. That is, if someone can get physical access to your data, data stored on disk may not be encrypted by default (as is the case with Azure). Considering that SQL Azure does not yet support encryption either, this becomes a security decision to be considered in the planning and design of your solution. As with any security implementation, the risks must be measured, weighed and mitigated.
But that doesn’t mean cloud platforms in general—and Azure in particular—are inherently not secure. What other options may be available to you?
One thing to note right away is that no application should ever use any of the keys provided by Azure as keys to encrypt data. An example would be the keys provided by Azure for the storage service. These keys are configured to allow for easy rotation for security purposes or if they are compromised for any reason. In other words, they may not be there in the future, and may be too widely distributed.
Storing your own key library within the Azure Storage services is a good way to persist some secret information since you can rely on this data being secure in the multi-tenant environment and secured by your own storage keys. This is different from using storage keys as your cryptography keys. Instead, you could use the storage service keys to access a key library as you would any other stored file. This is fairly straightforward to implement. For example, say you wanted to implement your own key library as a simple text file to persist some secret information. This would be best stored as data in the blob service API as opposed to either the queue or table storage service. The blob area of the storage service is the best place for data such as binary audio and images or even text files. The queue portion of the service is focused on secure messaging for small data objects that do not persist for long periods of time. The table storage system is great for structured data and information that needs to be persisted and accessed in specific parts, identical to relational data in a database.
You start by persisting a key in a CSP key container. This is a great option for storing a public key that is difficult to retrieve without physical access to the server. With Azure, where the location of applications and data is abstracted, this would make even a public key stored in this manner extremely difficult to find and retrieve. The creation of a key storage container is very simple; here is an example using the RSA provider that creates our key. If the key container already exists, its key is loaded into the provider automatically:
CspParameters CspParam = new CspParameters();
CspParam.KeyContainerName = "SampleContainerName";
RSACryptoServiceProvider RSAProvider = new
RSACryptoServiceProvider(CspParam);
There are also other options you can consider based on your needs. For example, you can use specific flags to secure the key to the user that created the container. This can be done with the use of CspParameters flags member:
CspParam.Flags = CspProviderFlags.UseUserProtectedKey;
Now create a request to the blob API using your Azure storage key. The request itself requires both a signature string as well as a proper request header. The proper header format is:
Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"
In this case, I want to maximize the security of my persisted secret data, so I’ll use the SharedKey authorization method. The signature portion of the header is a hash-based authentication code that is generated by the SHA256 algorithm and your storage key against the data in the signature. This hash is then encoded into a base64 string. A sample signature might look like this:
"PUT\n\ntext/plain; charset=UTF-8\n\nx-ms-Date:Fri, 12 Sep 2009 22:33:41 GMT\nx-ms-meta-m1:v1\nx-ms-meta-m2:v2\n/exampleaccount/storageclientcontainer/keys.txt"
As described earlier, I would then generate the base64 encoded hash and use that in the header as the signature. This key file then could only be accessed by those who have an application that runs in your application space in the Azure cloud with access to your storage keys. So with key persistence, you can either manage the keys outside the Azure framework or inside the cloud itself.
Key and Security Threats
One item worth covering at least briefly is key security. This is a slightly different topic than how you persist and store keys. Keys themselves are essentially strings of characters that have a very high level of entropy, meaning an extremely high level of randomness. In fact, this can lead to a common attack process to find keys within a system. For instance, if you take a dump of memory or an area of data on a hard disk, the areas of extremely high entropy are great places to start mining for keys.
Apart from choosing good security practices based on the needs of your application and securing your data, how else can you protect yourself? To start, always assume that the processes you’re using to decrypt, encrypt and secure data are well-known to any attacker. With that in mind, make sure you cycle your keys on a regular basis and keep them secure. Give them only to the people who must make use of them and restrict your exposure to keys getting outside of your control.
Finally, invest time in diagramming the flow of your data, both secure and unsecure. Take a look at where your data goes and how, where you store secrets, and especially where your data crosses boundaries such as public and private networks. This will give you a good idea of where your data is exposed, and allow you to target those risks with plans for mitigating them in a straightforward manner.
A related question I’ve been asked is whether Azure supports SSL. The short answer to this is yes! Azure would not be a very capable cloud platform for Web-based services and applications without support for SSL.
Encryption with SQL Azure
The release of SQL Server 2008 introduced a new feature: transparent data encryption (TDE). For the first time, SQL Server can encrypt its data fully with very little effort needed beyond what was required for the limited encryption available in SQL Server 2005. However, the initial version of SQL Azure storage does not yet support database-level encryption, though it’s a feature being considering for a future version. It should be noted that SQL Azure is only available via port 1433 and only via TCP connections; it currently cannot be exposed on other ports.
Even though this feature is not yet integrated into Azure, there are several security features of SQL Azure that the developer or designer should keep in mind. First of all, SQL Azure supports the tabular data stream (TDS). This means you can for the most part connect and interact with the database just like you’ve always done. Taking advantage of ADO.NET encryption and trusted server certificates is definitely worth considering, especially when accessing your SQL Azure database from outside the cloud.
The connection properties Encrypt=True and TrustServerCertificate = False, in the proper combination, will ensure data transmission is secure and can help prevent man-in-the-middle attacks. This is also a requirement for connecting to SQL Azure—you cannot connect to SQL Azure unless connection-level encryption has been turned on.
The second security feature of SQL Azure you should familiarize yourself with is the SQL Azure firewall. This tool will be very familiar to those who have used local software firewalls or even SQL Server security surface-area toolsets. It lets you allow or prevent connections from various sources, all the way down to specific IP addresses or ranges. The SQL Azure firewall can be managed via the SQL Azure portal or directly in the master database with the provided stored procedures such as sp_set_firewall_rule and sp_delete_firewall_rule.
As with any implementation of SQL Server, user account management is another aspect that must be tightly controlled. The firewall within SQL Azure is indeed a great tool, but it should not be relied on by itself. User accounts with strong passwords and configured with specific rights should be used as well to complement your data security model.
These new tools go a long way toward making SQL Azure a very tightly secured managed platform for cloud-based applications. If you’re trying this service out for the first time, remember that before you can connect, you must initially configure the SQL Azure firewall. This must first be done through the SQL Azure Web portal, but can be done later directly in the master database as described earlier.
Immutability and In-Memory Resources
Immuta-what? Immutability in object-oriented programming simply means that the object’s state cannot be modified after its initial creation. A concrete example in the Microsoft .NET Framework is the string class. When the value of a string is changed in code, the original string in memory is simply abandoned and a new string object is created to store the new value.
Why is this important from a security perspective? Well, that string may stay in memory for as long as the server is online without a reboot. You really have no way of knowing with certainty how long a string will stay in memory. This is important when considering how to store information in code such as cryptographic keys or copies of encrypted and decrypted data. By leaving a trail of that data behind you in memory, you leave behind information that exposes your secrets to the clever data thief.
Because of this vulnerability, it is always recommended that such data be stored in buffers such as byte arrays. That way, as soon as you’re done with the information, you can overwrite the buffer with zeroes or any other data that ensures the data is no longer in that memory.
Because Azure is a cloud environment I’ve been asked if this is a still a concern, and it’s a good question. True, in the Azure system individual applications are isolated from each other. This makes exposing data in memory much less of an issue in general. It would be very difficult to associate applications and memory space in the cloud. However, I still recommend the cautious approach and cleaning up after yourself. You may not always run this piece of code in the cloud, and other vulnerabilities may expose themselves in the future. While less of a concern, keep this habit, and persist this approach.
In Figure 3 I’ve modified the previous example that generated random integers. Here I added a bit of error-handling to ensure that I have a finally block that always runs, no matter what. Within that block I am doing a very simple iteration through the values in the byte array, overwriting each position with a zero. This overwrites that data in memory because byte arrays are mutable. I know that this number is no longer in memory owned by the execution of this member. This can be done to any byte array used as a data buffer for items such as keys, initialization vectors, and encrypted or decrypted data.
Figure 3 Clearing Data from Memory
public static int GenerateRandomNumber() {
byte[] GeneratedBytes = null;
try {
GeneratedBytes = new byte[4];
RNGCryptoServiceProvider CSP =
new RNGCryptoServiceProvider();
CSP.GetBytes(GeneratedBytes);
return BitConverter.ToInt32(GeneratedBytes, 0);
}
finally {
for (int x = 0; x < GeneratedBytes.Length; x++) {
GeneratedBytes[x] = 0;
}
}
}
Message Queues
Azure queues provide a similar set of functionality to the Microsoft Message Queuing (MSMQ) services that are common to enterprise Windows applications. The message queue service within Azure stores text-based messages no larger than 8 KB in a first-in, first-out (FIFO) manner. This allows services and applications running on different servers—or in this case within the cloud—to interact and send actionable messages to each other in a secure and distributed manner.
There are five basic functions that allow you to push a message to the queue, peek a message, pull a message and so on. The question that has come up most often is, how secure are these messages?
Many of the features currently supported by MSMQ are not yet supported within the Azure messaging APIs. However, there are similarities. As with the blob data service, the messaging services makes use of the same REST get and put interfaces. Writing and reading messages can be done either in code or with a URI and Web request calls that can be encrypted via SSL for requests over unsecured networks. This means transmission of requests is encrypted.
Also, as with the other storage services within Azure, any access to a message queue must make use of the same storage service key. Only applications with access to the key can view or add messages to these queues. This makes any additional encryption of these messages overkill unless the body of these messages is going to be leaving the secured network or secured application space.
Wrapping It All Up
In today’s drive toward service-oriented architecture and solutions, few can consider doing business without cloud applications. The isolation of data and services in a multi-tenant environment such as Azure is one of the major concerns of anyone who has an eye toward using private data.
As with any new platform, security and cryptography features will continue to evolve in Microsoft Azure. Microsoft has taken great pains to not only provide a secure, isolated environment, but also to expose what it has done to allow for public certification of these measures. This should give engineers confidence that Microsoft wants to be a closer partner on security and keeping systems and applications locked down.
The whole point of security and especially cryptography is to make your information and processes very hard to gain access to. We can define “hard” as meaning that it is beyond the capability of any adversary to break into such a system for the duration of the life of that data or process. This is, however, a relative definition based on the requirements of the application or data being used. That is why I’ve continued to emphasize the need of constant evaluation of security and cryptographic requirements through this article. That is essential to ensuring these tools can be used effectively to make your cloud system secure and to protect your data.
Jonathan Wiggs is currently a principal engineer and development manager at Nuance Communications Inc. Contact Wiggs directly at Jon_Wiggs@yahoo.com.