views:

297

answers:

6

On a form of my web app, I've got a hidden field that I need to protect from tampering for security reasons. I'm trying to come up with a solution whereby I can detect if the value of the hidden field has been changed, and react appropriately (i.e. with a generic "Something went wrong, please try again" error message). The solution should be secure enough that brute force attacks are infeasible. I've got a basic solution that I think will work, but I'm not security expert and I may be totally missing something here.

My idea is to render two hidden inputs: one named "important_value", containing the value I need to protect, and one named "important_value_hash" containing the SHA hash of the important value concatenated with a constant long random string (i.e. the same string will be used every time). When the form is submitted, the server will re-compute the SHA hash, and compare against the submitted value of important_value_hash. If they are not the same, the important_value has been tampered with.

I could also concatenate additional values with the SHA's input string (maybe the user's IP address?), but I don't know if that really gains me anything.

Will this be secure? Anyone have any insight into how it might be broken, and what could/should be done to improve it?

Thanks!

+4  A: 

It would be better to store the hash on the server-side. It is conceivable that the attacker can change the value and generate his/her own SHA-1 hash and add the random string (they can easily figure this out from accessing the page repeatedly). If the hash is on the server-side (maybe in some sort of cache), you can recalculate the hash and check it to make sure that the value wasn't tampered with in any way.

EDIT

I read the question wrong regarding the random string (constant salt). But I guess the original point still stands. The attacker can build up a list of hash values that correspond to the hidden value.

Vivin Paliath
Thanks. I think I'm going to generate a random token using Rails' ActiveSupport::SecureRandom and store that in the DB along with the record id that is my important value.
Myron
re. store hash on server side -- if you could store something on the server, you might as well store the "important value" on the server side and ignore the hidden field logic -:)
sri
@Vivin re. attacker can change the value - No he can't. Note that the second hidden field is SHA1 (important value + random secret), and not SHA1 (Important value) + random secret. So, if the attacker accesses the page multiple times, he is going to see a totally different random string which he won't be able to edit/modify without knowning the random secret. The randomg secret is acting like a constant salt in this case.
sri
@sri That's right - I thought he meant `SHA1(value) + randomStr`, versus `SHA1(value + randomStr)`. Either way it's better to keep the hash on the server-side because the attacker can easily build a list of values and corresponding hashes.
Vivin Paliath
A: 

If you can't/won't store the hash server side, you need to be able to re-generate it server-side in order to verify it.

For what it's worth, you should also salt your hashes. This might be what you meant when you said:

concatenated with a constant long random string (i.e. the same string will be used every time)

Know that if that value is not different per user/login/sesison, it's not actually a salt value.

Broam
A: 

As long as you guard the "constant long random string" with your life then your method is relatively strong.

To go further, you can generate one time / unique "constant long random string"'s.

zaf
A: 

What you are describing is similar to part of the implementation required for what are termed canaries and are used to mitigate Cross Site Request forgery attacks.

Generally speaking, a hidden input inside a HTML form contains an encrypted value that is posted back with a HTTP request. A browser cookie or a string held in session contains the same encrypted value such that when the hidden input value is decrypted and the cookie/session value is decrypted, the unencrypted values are compared to each other - if they are not identical, the HTTP request cannot be trusted.

The encrypted value might be an object containing properties. For example, in ASP.NET MVC, the canary implementation uses a class that contains properties for the authenticated username, a cryptographically pseudo random value generated using the RNGCryptoServiceProvider class, the DateTime (UTC format) at which the object was created and an optional salt string. The object is then encrypted using the AES Encryption algorithm with a 256 bit key and decrypted with the same key when the request comes in.

Russ Cam
+1  A: 

Digital Signature

Its probably overkill, but this sounds no different than when you digitally sign an outgoing email so the recipient can verify its origin and contents are authentic. The tamper-sensitive field's signature can be released into the wild with your tamper-sensitive field with little fear of undetectable tampering, as long as you protect the private key and verify the data and the signature with the public key on return.

This scheme even has the nifty property that you can limit "signing" to very protected set of servers/processes with access to the private key, but use a larger set of servers/processes provided with the public key to process form submissions.

If you have a really sensitive "do-not-tamper" field and can't maintain the hash signature of it on the server, then this is the method I would consider.

Although I suspect most are familiar with digital signing, here's some Wikipedia for any of the uninitiated:

Public Key Cryptography - Security

... Another type of application in public-key cryptography is that of digital signature schemes. Digital signature schemes can be used for sender authentication and non-repudiation. In such a scheme a user who wants to send a message computes a digital signature of this message and then sends this digital signature together with the message to the intended receiver. Digital signature schemes have the property that signatures can only be computed with the knowledge of a private key. To verify that a message has been signed by a user and has not been modified the receiver only needs to know the corresponding public key. In some cases (e.g. RSA) there exist digital signature schemes with many similarities to encryption schemes. In other cases (e.g. DSA) the algorithm does not resemble any encryption scheme. ...

Bert F
+1  A: 

If you can't handle the session on the server, consider encrypting the data with your private key and generating an HMAC for it, send the results as the hidden field(s). You can then verify that what is returned matches what was sent because, since no-one else knows your private key, no-one else can generate the valid information. But it would be much better to handle the 'must not be changed' data on the server side.

You have to recognize that anyone sufficiently determined can send an HTTP request to you (your form) that contains the information they want, which may or may not bear any relation to what you last sent them.

Jonathan Leffler