Broken down, my requirements for obfuscation are:
- Serialize an object which contains my parameters (address and expiry), so that it can be transmitted to a different server (i.e. the web server)
- Encrypt the serialized bytes to prevent tampering
- Make Quotable the encrypted bytes so that they can be included within a URL querystring (albeit after a suitable UrlEncode)
Key Generation
My security requirements are pretty basic, as I'm not trying to protect anything desperately important here - rather prevent basic tampering by spotting patterns in the quotable string. Therefore, I'll keep things as simple as possible and take some shortcuts, for example using a basic security algorithm (DES), and embedding the encryption key within my source code.
Firstly, we'll need to generate a key upon which the encryption and decryption can occur:
using System;
using System.IO;
using System.Security.Cryptography;
namespace ObfuscationTesting
{
class Program
{
static void Main(string[] args)
{
// Generate a suitable key
DESCryptoServiceProvider desCryptoServiceProvider =
new DESCryptoServiceProvider();
desCryptoServiceProvider.GenerateKey();
// Represent key as a quotable string
string base64EncodedKey =
Convert.ToBase64String(desCryptoServiceProvider.Key);
// Save to file
File.WriteAllText(@"C:\key.txt", base64EncodedKey);
}
}
}
Obfuscator Class
Now, we'll create a static class that takes any serializable object and returns us a string which represents that object (and, naturally, allows us to re-create the original object from such a string):
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
namespace ObfuscationTesting
{
public static class Obfsucator
{
// The key previously generated
private const string KEY = "MXvUAmUobjA=";
public static string Obfuscate(object targetObject)
{
// Binary serialize
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, targetObject);
byte[] serialized = memoryStream.GetBuffer();
// Encrypt
DESCryptoServiceProvider desCryptoServiceProvider =
new DESCryptoServiceProvider();
desCryptoServiceProvider.Key = Convert.FromBase64String(KEY);
desCryptoServiceProvider.IV = Convert.FromBase64String(KEY);
byte[] encrypted = desCryptoServiceProvider
.CreateEncryptor()
.TransformFinalBlock(serialized, 0, serialized.Length);
// Render as string
string quotable = Convert.ToBase64String(encrypted);
return quotable;
}
public static object Deobfuscate(string quotable)
{
// Retrieve the bytes from the quotable string
byte[] encrypted = Convert.FromBase64String(quotable);
// Decrypt
DESCryptoServiceProvider desCryptoServiceProvider =
new DESCryptoServiceProvider();
desCryptoServiceProvider.Key = Convert.FromBase64String(KEY);
desCryptoServiceProvider.IV = Convert.FromBase64String(KEY);
byte[] serialized = desCryptoServiceProvider
.CreateDecryptor()
.TransformFinalBlock(encrypted, 0, encrypted.Length);
// Deserialize
MemoryStream memoryStream = new MemoryStream(serialized);
BinaryFormatter binaryFormatter = new BinaryFormatter();
object deserialized = binaryFormatter.Deserialize(memoryStream);
return deserialized;
}
}
}
Usage
We'll create a class which contains the parameters I'd like to represent, remembering to mark it as serializable:
using System;
namespace ObfuscationTesting
{
[Serializable]
public class Token
{
public string EmailAddress { get; set; }
public DateTime Expiry { get; set; }
}
}
To construct the URL with the obfuscated parameters, all we need to have is:
(Assembly reference: System.Web)
using System;
using System.Web;
...
Token token = new Token()
{
EmailAddress = "foo@bar.com",
Expiry = DateTime.Now.AddHours(1)
};
string url = String.Format(@"/sendMessage.aspx?token={0}",
HttpUtility.UrlEncode(Obfsucator.Obfuscate(token)));
And, on our web page that receives the URL, we just need to de-obfuscate again:
string tokenString = Request.QueryString["token"];
Token token = (Token)Obfsucator.Deobfuscate(tokenString);
if (token.Expiry < DateTime.Now)
{
// Token has expired
Response.End();
}
else
{
string toAddress = token.EmailAddress;
// etc
}
No comments:
Post a Comment