Protecting Cookies: Once and For All
Posted
by Your DisplayName here!
on Least Privilege
See other posts from Least Privilege
or by Your DisplayName here!
Published on Wed, 01 Jun 2011 05:04:53 GMT
Indexed on
2011/06/20
16:38 UTC
Read the original article
Hit count: 317
ASP.NET
|IdentityModel
Every once in a while you run into a situation where you need to temporarily store data for a user in a web app. You typically have two options here – either store server-side or put the data into a cookie (if size permits).
When you need web farm compatibility in addition – things become a little bit more complicated because the data needs to be available on all nodes. In my case I went for a cookie – but I had some requirements
- Cookie must be protected from eavesdropping (sent only over SSL) and client script
- Cookie must be encrypted and signed to be protected from tampering with
- Cookie might become bigger than 4KB – some sort of overflow mechanism would be nice
I really didn’t want to implement another cookie protection mechanism – this feels wrong and btw can go wrong as well.
WIF to the rescue. The session management feature already implements the above requirements but is built around de/serializing IClaimsPrincipals into cookies and back. But if you go one level deeper you will find the CookieHandler and CookieTransform classes which contain all the needed functionality.
public class ProtectedCookie
{
private List<CookieTransform> _transforms;
private ChunkedCookieHandler _handler = new ChunkedCookieHandler();
// DPAPI protection (single server)
public ProtectedCookie()
{
_transforms = new List<CookieTransform>
{
new DeflateCookieTransform(),
new ProtectedDataCookieTransform()
};
}
// RSA protection (load balanced)
public ProtectedCookie(X509Certificate2 protectionCertificate)
{
_transforms = new List<CookieTransform>
{
new DeflateCookieTransform(),
new RsaSignatureCookieTransform(protectionCertificate),
new RsaEncryptionCookieTransform(protectionCertificate)
};
}
// custom transform pipeline
public ProtectedCookie(List<CookieTransform> transforms)
{
_transforms = transforms;
}
public void Write(string name, string value, DateTime expirationTime)
{
byte[] encodedBytes = EncodeCookieValue(value);
_handler.Write(encodedBytes, name, expirationTime);
}
public void Write(string name, string value, DateTime expirationTime,
string domain, string path)
{
byte[] encodedBytes = EncodeCookieValue(value);
_handler.Write(encodedBytes,
name,
path,
domain,
expirationTime,
true,
true,
HttpContext.Current);
}
public string Read(string name)
{
var bytes = _handler.Read(name);
if (bytes == null || bytes.Length == 0)
{
return null;
}
return DecodeCookieValue(bytes);
}
public void Delete(string name)
{
_handler.Delete(name);
}
protected virtual byte[] EncodeCookieValue(string value)
{
var bytes = Encoding.UTF8.GetBytes(value);
byte[] buffer = bytes;
foreach (var transform in _transforms)
{
buffer = transform.Encode(buffer);
}
return buffer;
}
protected virtual string DecodeCookieValue(byte[] bytes)
{
var buffer = bytes;
for (int i = _transforms.Count; i > 0; i—)
{
buffer = _transforms[i - 1].Decode(buffer);
}
return Encoding.UTF8.GetString(buffer);
}
}
HTH
© Least Privilege or respective owner