I have a message that I am passing to myself which will be subject to man-in-the-middle attacks. Because of that, I am concerned about the integrity of the message being maintained between the time I send it, and the time that I receive it back.
It should be assumed that once I send the message to myself, no information about the message sent will be available to me in the future. The message is completely self-contained.
To that end, I know that should hash the message contents and compare the hashes before I send the message, and after I send the message, if they differ, then the message has been tampered with.
Of course, if the man-in-the-middle knows that the hash is really just the hash of the message contents, as-is, then because the message is self-contained, he can just create new contents and apply the same hash algorithm to the contents.
The question is, to what lengths should I go to randomize the message contents when generating the hash? When does it reach the point of diminishing returns?
In this scenario, I have a set of key/value pairs. To that end, the steps that I know I HAVE to take are:
- Add a salt to the message. The salt is a secret to the rest of the world. It gets attached to the contents of the message before hashing.
- Order the key/value pairs in a consistent manner before generating the hash.
- While not directly relevant, a timestamp is going to be added to the contents of each message before hashing, to prevent replay attacks.
These are the optional steps that I am considering:
- Transforming the keys before I order them. I've considered reversing them, then ordering by count/key.
- Playing with the separators that separate key/value pairs (both for the separator for the key/value and the separator for the pair).
NOTE
Message privacy is not a requirement here, so I am not looking for encryption. The values must be transmitted in plain-text.
Finally, what hashing algorithms should I avoid?
Specifics
I have an ASP.NET MVC site which I have a controller which handles input validation and persistence.
If (based on a heuristic, it's not important which) the input is determined to be an automated spam attempt, a model of IDictionary<string, string>
is created with the input values and a ViewResult is sent to a general CAPTCHA page.
In that view, in the form that contains the CAPTCHA control, the contents of the IDictionary<string, string>
will be written out in hidden input fields, and the action of the form will be the same action that the contents were originally posted to. This way, MVC can pick up the values when the form is resubmitted.
It's because of this I can't encrypt the key/value pairs (or maybe I can and should, tell me why and how!).
Of course, I need to add one more value, which contains the hashed message contents. If that value is there, then the controller will check to see that the message integrity is maintained, and allow the input to be persisted if it has.
Solution
I've opted to go with the SignedCms class in the System.Security.Cyrptography.Pkcs namespace, which represents the signging and verifying of CMS/PKCS #7 messages.
To elaborate, I've created a self-issued certificate with MAKECERT.EXE and then in my code, I use the example here to digitally sign the data:
http://blogs.msdn.com/shawnfa/archive/2006/02/27/539990.aspx
Now, it should be a matter of keeping the password on the exported private key secure, as well as security on the server, which makes it less about programming.
I'll have to add an extra key for the timestamp for replay attacks, but that won't be too hard.
The answer goes to Kalium, not for his initial post, but for his follow up comments which pointed the way to digital signatures, and eventually my discovery of how to utilize them in .NET.
Thanks to everyone who contributed.