Hi !
There's a law here in France, that forces all business touse "cash register software" or ledger software that are "tamper proof" :
The laws can be summarized as follow:
- The user cannot delete or update any existing record (High level tamper protection) : that's easy to do, just don't implement delete or update functions ;)
- Neither a developper or a DBA can tamper the database records (low level protection). At least, the company editing the software must be able to show , when audited, proofs that the records have not been tampered. This can be done with record chaining and hashing/signing.
Those laws are ensuring that no business is trying to avoid paying VAT on any of the business done (just imagine, as a business owner, you delete or update sale_lines after the sale occured, reduce the value, and so, you can "pay" less VAT... that's a thing that some shop owners used to do !)
I'm a bit disappointed for the second rule : Even if the user doesn't have access to the DB, if at any time, he can pay a hacker that could get into the server, it would grab the DB password in the .env file, and could eventually manage to go into the "sale_lines" table and modify it.
So, the first way I've found could be : sign every record with a hash (sha256 ie.), store this signature into every record. Then, on the "next record", reference the previous record signature. Add an "foreign key" that self-references the same table, previous_record_hash = record_hash or something similar....
That's a first step, but it's not enough : "evil programmer" could still update records, recompute hashs and cascade-update next records !
Next step would be: Let the DB the hash computation with a "generated column". Mysql allows it since a few years ago, and the version I use allows it. That's a bit better, if you try to update the record, since the hash is referenced by the next record, you would have to recompute every next records in the same instruction to allow the update to pass....
Another step could be : use HMAC-like signing functions instead of just hashing : this involves using a private key... But gess where would be the private key ? In an .env file ? In the code ? That's not a good idea... "evil programmer" would have access to the key and then could also re-sign the records ! An alternative could be to make the sign process on another server, with an API call... Passing the "data to sign" to server B from server A, just getting the hmac signature and storing it. There's no constraint in the DB that would avoid tampering the data, but the law requirements are met: if anyone tampers the data, the stored signature would become invalid : checking each row against the "signature server" would show that row signature mismatches the "signature server" version, and the law guys would be happy ?
Having the "previous hash" referenced in each rows shows that no row where deleted by a DBA, but....
If anyone deletes many rows starting from the end and then reinsert new rows.... How would you proove that data has not been tampered !?
In facts, the perfect solution would be an append-only table for the sale_lines data ! But I haven't found anything that was satisfying under mysql features !?