Securing Query Strings with SecureQueryString 2.0
Almost a year ago I wrote
an article about securing query strings. With that article I released
the free component: SecureQueryString 1.0. If you haven’t already, I highly
recommend that you take the time to read
this article to get an overview as well as important guidance on usage.
Today I have officially released SecureQueryString 2.0. In this article I will
focus on the new things that you can do in 2.0.
Sample code can be downloaded here.
What’s new?
Version 1.0 included basic encryption functionality as well as query string
expiration. Here’s what’s new in version 2.0:
-
Signs data to prevent tampering
-
Implements any SymmetricAlgorithm and HashAlgorithm to be used
for cryptographic functions. Default is AES (RijndaelManaged) and MD5 (MD5CryptoServiceProvider)
respectively.
-
Supports pluggable cryptographic interfaces for use with 3rd party
cryptographic libraries (see ISymmetricCryptoProvider and IHashProvider
for implementation details).
-
Rewritten from the ground up using
TDD
(Unit tests are included in the source download).
-
Complete reference documentation in CHM
While the API is not backwards compatible with version 1.0, it’s very similar.
Here’s an example of a simple implementation:
SecureQueryString qs =
new
SecureQueryString(Key);
qs["SomeParameterName"] = SomeParameterValue;
Response.Redirect("Sample.aspx?x=" + HttpUtility.UrlEncode(qs.ToString()));
The only difference here is that the constructor requires that you pass a key in
for the cryptographic functions. Retrieving the query string is similar:
SecureQueryString qs =
new
SecureQueryString(Key, Request["x"]);
SomeParameterValue = qs["SomeParameterName"];
Of course there are optional steps that exploit new features, but the basic
functionality is virtually identical.
Signing
In version 1.0, tampering was theoretically because someone could modify the
encrypted information. While this is highly unlikely unless the attacker
cracked your key, there are methods that would allow the attacker to at least
corrupt the query string. Signing via a hash algorithm (i.e. MD5) makes it
virtually impossible for an attacker to manipulate the data within a query
string. The downside to signing is that it makes an already bloated query
string even larger. Fortunately this version supports any hash algorithm for
signing (more on this later), so you can choose to use a weaker algorithm to
reduce the payload.
Flexible cryptography
By default the AES algorithm is used for encrypting the query string, and MD5 is
used for signing. With minimal effort you can use any .NET or 3rd party
algorithm that inherits from System.Security.Cryptography.SymmetricAlgorithm
for encrypting the query string. The same goes for any algorithm that inherits
from System.Security.Cryptography.HashAlgorithm for signing the query
string. The following example illustrates this by used the Triple DES and SHA1
algorithms:
/* First we must create a SymmetricAlgorithmProvider. .NET has a
provider which creates the Triple DES object for us, so we’ll use that as well
as the key we wish to use. */
SymmetricAlgorithmProvider 3DESProvider =
new
SymmetricAlgorithmProvider(
TripleDESCryptoServiceProvider.Create(),
Key
);
/* Second, we’ll need a HashAlgorithmProvider. This is very
similar to creating the SymmetricAlgorithmProvider for Triple DES.
*/
HashAlgorithmProvider sha1Provider =
new
HashAlgorithmProvider(
SHA1CryptoServiceProvider.Create()
);
/* Finally, we will create the SecureQueryString and
instruct it to use our providers as opposed to the defaults. */
SecureQueryString qs =
new
SecureQueryString(Key);
qs.SymmetricCryptoProvider = 3DESProvider;
qs.HashProvider = sha1provider;
But what about 3rd party components that are not managed, such as a
COM component? SecureQueryString 2.0 supports “pluggable” cryptography
components via the ISymmetricCryptoProvider and IHashProvider interfaces.
This means that you can create a managed wrapper that implements the
appropriate interface for your unmanaged 3rd party components. Even
if the component is managed, it may not inherit the respective .NET
cryptography base classes. Some of you will notice that this loosely follows
the Provider Design Pattern. This allows for extensibility
without the need for “white box” code changes, or changes to the
SecureQueryString source.
Let’s take a look at how simple this type of extensibility is. For the purposes
of this example, we’ll use a mock hashing component called SuperSecureHash.
This component is written in managed code, but it does not inherit from HashAlgorithm.
SuperSecureHash has a method called GenerateHash(string), which
takes a string and returns the hashed value as a byte[]. First we
need to create a SuperSecureHashProvider class, and implement IHashProvider.
Then, we’ll need to fill out the required Hash(byte[]) method. Since IHashProvider.Hash
takes a byte[], we’ll need to convert the input to a string so
that we can pass it to SuperSecureHash.GenerateHash. To do this, we will
use System.Encoding.Unicode.GetString.
public class
SuperSecureHashProvider : IHashProvider {
public byte[] Hash(byte[]
buffer) {
SuperSecureHash hasher =
new SuperSecureHash();
string hashString =
System.Encoding.Unicode.GetString(buffer);
return hasher.GenerateHash(hashString);
}
}
Our provider for SuperSecureHash is complete. Now all we have to do is tell
SecureQueryString to use our provider for hashing as opposed to the default:
SuperSecureHashProvider myHashProvider =
new
SuperSecureHashProvider();
SecureQueryString qs =
new
SecureQueryString(Key);
qs.HashProvider = myHashProvider;
That’s it! In just a few easy steps we’ve been able to plug-in our own hashing
algorithm without touching any of the existing source code. If a new version of
SecureQueryString comes out, we won’t have to worry about merging our custom
code.
Conclusion
Version 2.0 of SecureQueryString gives us a lot more control over how we want to
protect query strings. It’s also a nice example of
TDD in practice. SecureQueryString is public domain and comes with the
entire source including all unit tests. If you have any questions please post
to this article’s forum so that everyone can benefit from the discussion. I
will also post updates to this component to the forums, as well as my
blog.