views:

304

answers:

6
+3  Q: 

Tamperproof table

I'm designing a table that will be used to store information on which customers will be charged.
The problem is the database could be on the customers servers.
I was thinking of adding a second table containing a hash of the first, so that the software using the database can update the database and the hash, but the customers can't edit (without us knowing) the table containing the chargeable information (on the basis that they can't generate the correct hash).

Is this a good way of stopping customers tampering with a table they have access to?
How would I create a hash of all the data within the table (possibly more than one table)?

Specifically I would need to hash the data within the table instead of an object such as a dataset (i.e. I don't want all the hashes to change if we change componants).

I was considering writing the data to a text file and creating a hash of the file, but that would be painfully slow as the table could contain upto 500,000 records and the hash would need to be generated on every update!

The implementation for this can be in either delphi or c#.

A: 

I would probably start off by looking at this. It is how to encrypt columns in SQL Server:

http://msdn.microsoft.com/en-us/library/ms179331.aspx

The problem you are going to have is that at some point in time, you are going to have to decrypt it. Since it is on their machines, and the code that you are using to verify the information is on there too, it will never be truly tamper proof. They could look through your compiled code and figure out how to bypass the security mechanisms you have in place.

You should also look at code obfuscation for .Net http://msdn.microsoft.com/en-us/magazine/cc164058.aspx It won't stop them, but it will slow them down.

Kevin
A: 

I'm not quite sure if this solves the bigger problem ( for example, it won't help if someone were to restore the server using a virtual machine image to an earlier date to lose the last few days of charges ) but

  1. many hash algorithms are designed as message digests so work incrementally such that Hash(concat(M,N), seed) == Hash(N,Hash(M,seed))

  2. you can log every SQL update command sent to the database

which should give you a cheaper hash independent of any C# components.

Pete Kirkham
Strangely enough it doesn't matter if they revert the VM/Database as they would have lost the work which we charge them for... sort of a self induced refund. But this is probably how I'll be hashing the individual rows.
JamesB
A: 

We have a column that contains a string value that is a very simple hash from several system-related values - license expiration date and others. This value is recalculated on-the-fly by the program at key junctures. If the recalculated value doesn't match, we barf & stop processing. The column is also used the key for encrypting other pieces of data. The procs used to do the recalculation are encrypted.

Not foolproof, but more work than anyone has wanted to go through to cheat. So far.

DaveE
A: 

Your hash solution does sound like it would work well enough. If performance is a concern, then only validate your hash with random samples, or use a scheduled job to update a hash validated date (which could be encrypted). If you use update triggers to clear your hash values, you can easily determine which records were "modified" and your program can verify that the trigger is still in place each time it runs.

skamradt
+10  A: 

Hashing would work, but there is another element you are missing. In order to generate a hash value that cannot be regenerated, then you need to include a secret value in the hash. A GUID would work, but it can really be any value that your customer doesn't know. Of course if they have the software on their computer, then they could conceivably discover the algorithm and the secret value, thus circumventing it. Using a different secret value for each customer would be a nice extra level of protection just in case one customer discovers it.

Example:

Hash([Table Data] + [Secret Value]) = [stored hash]

If you only do a hash of the table data, then they can simply rehash the modified table data, and then you won't know they have done it.

As far as how to hash the entire table, instead of each row, most hashes support incremental hashing. The reason is if you are hashing a 10 GB file, you probably don't have enough RAM to load it all into memory to work on. Instead you hash one block at a time, and then when you are done you finalize the hash. You can use this same method on your data. Simply add each row to the hash, one at a time, and when you are done finalize the hash. Of course remember to include your secret key value.

Some things to keep in mind:

  1. Don't include the validation routine in your application - in other words don't provide a way for them to validate that their attempted hack worked or failed. Doing so will give them immediate feedback and allow them to engineer an eventual solution.
  2. Make sure you test this system well, especially if you plan to penalize your customer if these hashes fail.
  3. Make sure you customer is aware that tampering with these values is forbidden. It might be a good idea to combine it with table level encryption to prevent accidental modifications.
  4. You might be better off logging the charges off site. If the server is connected to the internet, then have it send the information to a web service you run. For even better security have the web service validate the messages and respond with a key. Then you can validate that key locally to make sure they haven't circumvented the web service. This would be a great application of public key message signing.

Also keep in mind that most people are honest, and many people will only resort to circumvention if the path of honesty is too painful (i.e. false positives on the tests, or too expensive of a price tag.) Often times those who would steal it wouldn't pay for it anyway. Others who might steal today may pay tomorrow. You don't want to battle your customers and treat them as your enemy because then you both loose.

Jim McKeeth
I like the idea of an encrypted string field, which your application (C#/Delphi/other) decodes to determine the numeric value, if this was delphi, I would use a Calculated Field to contain the real price. An invalid or modified price code field would result in a Calculated Field value of $10,000,000,000.00. So they modify the table, and you $$$profit$$$. :-) Just kidding.
Warren P
If I'm updating the hash when posting new records, how do I avoid overwriting an invalid hash with a valid one without validating the hash?
JamesB
@JamesB - Excellent question. Instead of overwriting it, add a new row that overrides it - something like a column for "replaces" that is a Foreign Key back to the same table.
Jim McKeeth
@JamesB - Does that answer your questions, or is there something more?
Jim McKeeth
Sorry I haven't had time to investigate the answers posted (as ever I've been shifted onto something else for the time being) I'm hoping to have anothing look at this within a couple of days.I Do have a slight issue with the speed of hashing an entire table (running a query pulling out each row and hashing effectively a concatinated string of values incrementally), this took 10 minutes on a large dataset, but perhaps I wasn't using the fastest method.
JamesB
A: 

Why not just hash on a per record basis, rather than per table?

You could concatenate some of the fields together, pad to say 64 characters (using a specific character or string of characters) then restrict to 64 characters and calculate the hash - write this into another table:

recordid  tableid  hash

job done - easy enough to calculate on the fly and easy enough to check.

Plus a random string of characters would be hard to find if they simply browsed your executable (assuming delphi here)

MarkRobinson
The problem with this is they can just delete the whole record, hash and all.
JamesB
Ah yes. You could perhaps then have fields in this table that then link other tables, if they deleted the record then data in other tables would be orphaned?
MarkRobinson
hmmm, I think I'll go the other way and store a hash on the parent (cumulative of the children) that way deleting a row invalidates the parent hash.
JamesB
This is close enough to the planned implementation that I'll accept it as the answer.
JamesB