I have a database table that I'd like to prevent a user from modifying the values/rows. How can I accomplish this?
Here are some criteria:
The table to be protected has a single column with data stored in plain text.
Other columns can be added to the table if needed to help protect the single column.
My application needs to have the ability to add, edit, and delete the values/rows in the table.
For this question, I am assuming the user has full and direct admin/read/write access to the database, i.e. the user can log into the database the execute queries directly.
If the user changes the values directly in the database, my application needs to flag that this has happened when it inspects the table.
There are other tables in the database, but they do not need to be protected in this manner. They can be used, if needed, to help protect the first table.
A database engine agnostic solution would be nice, but I am using SQL Server 2005 or later.
For example:
Let's say my table has 3 rows with data "A", "B", "C". My application should be able change the values to "A", "B", "D", but not my user (through direct modification of the database). Additionally, I expect that if my application changes the values to "A", "B", "D", the user cannot edit the table directly to go back to "A", "B", "C". If that happens, the application will flag that the table has been tampered with.
all i can think of here, would be some sort of MAC or signature schema
derive a hash from the data in the columns you want to protect, plus a secret value to obtain a message authentication code, and store that in another column ...
your application can calculate that MAC again when you want to test for integrity ...
problem: that secret needs to be stored somewhere ...
you can also setup this as a digital signature schema where you application only holds the verification key, and a service somewhere will timestamp hash and sign the data for your application ... that way, you only have to keep the secret key of that service a real secret ...
Related
I need to store some user data in one server database. It is not sensitive data, but neither I or other users should get access to this content. I want the owner to be confident that even I or other developers will not be able to read his data querying the database.
Tables structure will be like this: (ID | Data) where Data should contain an encrypted JSON string.
I would like to do encryption/decryption on the client, using a secret key that would not persisted anywhere (user should keep this password or otherwise he would not be able to get his data anymore).
Do you see any pitfall in this approach?
I'm starting a new application and was wondering what the best method of logging is. Some tables in the database will need to have every change recorded, and the user that made the change. Other tables may just need to have the last modified time recorded.
In previous applications I've used different methods to do this but want to hear what others have done.
I've tried the following:
Add a "modified" date-time field to the table to record the last time it was edited.
Add a secondary table just for recording changes in a primary table. Each row in the secondary table represents a changed field in the primary table. So one record update in the primary could create several records in the secondary table.
Add a table similar to no.2 but it records edits across three or fours tables, reference the table it relates to in an additional field.
what methods do you use and would recommend?
Also what is the best way to record deleted data? I never like the idea that a user can permanently delete a record from the DB, so usually I have a boolean field 'deleted' which is changed to true when its deleted, and then it'll be filtered out of all queries at model level. Any other suggestions on this?
Last one.. What is the best method for recording user activity? At the moment I have a table which records logins/logouts/password changes etc, and depending what the action is, gives it a code either 1,2, 3 etc.
Hope I haven't crammed too much into this question. thanks.
I know it's a very old question, but I'd wanted to add more detailed answer as this is the first link I got googling about db logging.
There are basically two ways to log data changes:
on application server layer
on database layer.
If you can, just use logging on server side. It is much more clear and flexible.
If you need to log on database layer you can use triggers, as #StanislavL said. But triggers can slow down your database performance and limit you to store change log in the same database.
Also, you can look at the transaction log monitoring.
For example, in PostgreSQL you can use mechanism of logical replication to stream changes in json format from your database to anywhere.
In the separate service you can receive, handle and log changes in any form and in any database (for example just put json you got to Mongo)
You can add triggers to any tracked table to olisten insert/update/delete. In the triggers just check NEW and OLD values and write them in a special table with columns
table_name
entity_id
modification_time
previous_value
new_value
user
It's hard to figure out user who makes changes but possible if you add changed_by column in the table you listen.
So I have, what would seem like a common question that I can't seem to find an answer to. I'm trying to find what is the "best practice" for how to architect a database that maintains data locally, then syncs that data to a remote database that is shared between many clients. To make things more clear, this remote database would have many clients that use it.
For example, if I had a desktop application that stored to-do lists (in SQL) that had individual items. Then I want to be able to send that data to a web-service that had a "master" copy of all the different clients information. I'm not worried about syncing problems as much as I am just trying to think through actual architecture of the client's tables and the web-services tables
Here's an example of how I was thinking about it:
Client Database
list
--list_client_id (primary key, auto-increment)
--list_name
list_item
--list_item_client_id (primary key, auto-increment)
--list_id
--list_item_text
Web Based Master Database (Shared between many clients)
list
--list_master_id
--list_client_id (primary key, auto-increment)
--list_name
--user_id
list_item
--list_item_master_id (primary key, auto-increment)
--list_item_remote_id
--list_id
--list_item_text
--user_id
The idea would be that the client can create todo lists with items, and sync this with the web service at any given time (i.e. if they lose data connectivity, and aren't able to send the information until later, nothing will get out of order). The web service would record the records with the clients id's as just extra fields.
That way, the client can say "update list number 4 with a new name" and the server takes this to mean "update user 12's list number 4 with a new name".
I think they general concept you're working with is the right direction, but you may need to pay careful attention to the use of auto-increment columns. For example, auto-increment on the server is useless if the client is the owner of this ID. Instead, you probably want list.list_master_id to be an auto-increment. Everything else you've mentioned is entirely plausible, though the complexity may increase if there may be multiple clients per user. Then, the use of an auto-increment alone probably isn't sufficient. Instead, you may need a guid or a datatype that also includes a client identifier to prevent id collision.
Without having more details it would be difficult to speculate on what other situations you may need to consider.
SERVER:
list
--id
--name
--user_id
--updated_at
--created_from_device_id
Those 2 tables link all records, might be grouped in one table also.
list_ids
--list_id
--device_id
--device_record_id
user_ids
--user_id
--device_id
--device_record_id
CLIENT (device_id=5)
list
--id
--name
--user_id
--updated_at
That will allow you to save records as(only showing relevant fields):
server
list: id=1, name=shopping, user_id=1234
user: id=27, name=John Doe
list_ids: list_id=1, device_id=5, device_record_id=999
user_ids: user_id=27, device_id=5, device_record_id=567
client
id=999, name=shopping, user_id=567
This way they are totally unaware of any ID's, translations can be done quite fast and you can supply the clients only with information and ID's they know of.
I have the same issue with a project i am working on, the solution in my case was to create an extra nullable field in the local tables named remote_id. When synchronizing records from local to remote database if remote_id is null, it means that this row has never been synchronized and needs to return a unique id matching the remote row id.
Local Table Remote Table
_id (used locally)
remote_id ------------- id
name ------------- name
In the client application i link tables by the _id field, remotely i use the remote id field to fetch data, do joins, etc..
example locally:
Local Client Table Local ClientType Table Local ClientType
_id
remote_id
_id -------------------- client_id
remote_id client_type_id -------------- _id
remote_id
name name name
example remotely:
Remote Client Table Remote ClientType Table Remote ClientType
id -------------------- client_id
client_type_id -------------- id
name name name
This scenario, and without any logical in the code, would cause data integrity failures, as the client_type table may not match the real id either in the local or remote tables, therefor whenever a remote_id is generated, it returns a signal to the client application asking to update the local _id field, this fires a previously created trigger in sqlite updating the affected tables.
http://www.sqlite.org/lang_createtrigger.html
1- remote_id is generated in the server
2- returns a signal to client
3- client updates its _id field and fires a trigger that updates local tables that join local _id
Of course i use also a last_updated field to help synchronizations and to avoid duplicated syncs.
say I have 6 tables.
Workstation
Workstation_CL
Location
Location_CL
Features
Features_CL
I am currently using triggers to do inserts into the "_CL" version of each table with an additional field that denotes whether the change was an "UPDATE", "INSERT" or "DELETE".
the workstation table keeps track of the "modified_by" user. if a user updates the location of a "Workstation" object, the "Location" table gets updated as well as the "Workstation" table. the only modification to the Workstation table is the "modified_by" field so that I will know who made the change.
The problem I am having is when I think about pulling an audit report. How will I link records in the "Location_CL" to the ones in the "Workstation_CL" both are populated by separate triggers.
somehow my question portion was erased. sorry about that.
Question: how can I pull some type of unique identifier to have in both the "Workstation_CL" and the "Location_CL" so that I can identify each revision? for instance, when I pull all records from the "Location_CL" and I see all location changes, pulling the username from the "Workstation_CL" that made the location change?
Give each revision a GUID generated by the trigger. Populate a field (RevisionId) in both tables with the value.
You need 2, maybe 3 columns on each audit table.
1) Timestamp, so you know when the changes were made.
2) User changed, so you can track who made the changes - I assume that Location can change independently of Workstation.
3) You might need an identifier for the transaction, too. I THINK you can get an id from the DB, though I'm not sure.
I don't think you can have an effective report without timestamps and users, though, and I don't think you just have the user on one table.
During the trigger event, I was able to exec the following:
SELECT #trans_id=transaction_id FROM sys.dm_tran_current_transaction
which gives me the transaction id for the current operation.
with that, I am able to insert it in to the corresponding _CL table and then perform selects that will match the auto-gen id's.
Currently I'm performing a migration from a microsoft access database to an SQL Express 2010 database.
Basically, I have an Access application that searches a customer database. The access app is developed in 2 parts. An access front end on each client called application.mdb and a data backend on a windows 2008 server called data.mdb. The application.mdb has 3 linked tables to data.mdb. which holds customers and contracts and items. The customer table relates to the contracts table (one to many) and the contracts table relates to the items table (one to many)
I imported the tables from the data.mdb into the sql tables by the same name and created the same relationships and configured them to cascade. I then created an obdc connection on the clients and updated the 3 linked tables in application.mdb to point to the tables on the sql server.
I start the application and everything seemed to work great, I can see all the data perfectly and the performance increase was well worth the effort.
Then I found a problem, when I add a new customer to the database it autonumbers the customer table and the contracts table but not the items table.... Thus if I attempt to alters any of the items in the items table for new customers I can not. I get the following error "cannot add record(s); primary key for table "items" not in recordset" which makes sense because SQL had not autonumbered the items table.
I can't understand why....
Any help would be greatly appreciated.
Well, just manually adding record direct in the items view should tell you if the autonumber is working. You MUST get the autonumber working when you edit + use in direct table view.
As always these kinds of issues comes down to the details. One thing that's different when using a SQL based backend compared for access applications is the generation of auto numbers (primary key) does not occur on server based systems until record is actually saved. When working with the jet based back end, the auto number is available the instant the record is dirtied.
So I would check if you have some type of code or event running in the application that is attempting to use as primary key value before the record been actually saved.
Usually access does a pretty good job. For example when you build a form in access, and then have a sub form in access to edit child records (and a child table), then as a rule when the focus switches from a main form to a sub form, access will force a save of the main record. This thus means the primary key (auto number column) is now available for correct functioning of the relationship. Access can and will use this PK value and insert this value into the foreign key value column in this child table for you.
However access will only do above for you WHEN you correctly set up the link master and link child settings in the sub form control. As a general rule when building forms in regular access, Access can detect the settings required and insert the correct values into the link master and link child settings for you. However, the detection of the FK column will not occur with linked tables.
So when you use SQL server, you have to edit and set these values manually in the sub form control. So I would check your link master and link child settings in the sub form you're using to edit this data, and ensure that the correct values are set. If this is VBA code, then ensure the record is actually saved before attempt to use and grab a PK value.
I should point out that even in non SQL server based applications, it is the setting up of the link master + child settings in the sub form that allows access to setup and maintain this foreign key value for you. So access is always had the ability to insert these values for you, and it'll do so with you about having to write any code at all. So during the editing process to insert and maintain these values Access does all of the work for you (so it's not the data engine that inserts these FK values for you, but the user interface or in some cases code you write)
So access will not setup and insert these correct values unless you set up the link master + child settings in that sub form control.
I would simply check if your link master and child master settings are correct in any sub form control you are using here.
This sounds like a stupid answer but check the Items table to be sure that auto-numbering is turned on.
One of the things I would suggest whenever you migrate a Jet/ACE database to SQL Server is to thoroughly review the database design, e.g.: the implementation of keys and constraints, choice of data types, choice of indexes, etc. Jet/ACE is a very different thing to most SQL DBMSs so you shouldn't assume that a database design that worked well for Jet/ACE is automatically suitable for a SQL DBMS. Upsizing wizards won't always identify every possible issue.
In SQL Server the nearest equivalent of an "auto-number" is the IDENTITY property. Check to be sure which columns are IDENTITY in your tables and create an IDENTITY column if you need one.