SQL trigger for audit table - sql

I am writing a trigger to audit updates and deletes in tables. I am using SQL Server 2008
My questions are,
Is there a way to find out what action is being taken on a record without going through the selection phase of the deleted and inserted tables?
Another question is, if the record is being deleted, how do I record within the audit table the user that is performing the delete. (NOTE: the user connected to the database is a general connection string with a set user, I need the user who is logged into either a web app or a windows app)
Please help?

For part one, you can either set up separate triggers or have one trigger that checks the special tables INSERTED and DELETED to discriminate between updates and deletes.
For part two, there's no way around it in this case, you're going to have to get that username to the database somehow via your web/windows app. Unfortunately you can't communicate with the trigger itself, and with a generic connection string the DB doesn't have any idea who it's dealing with.
I've found that it can be helpful to add a "LastModifiedBy" column to the tables that you plan to audit so that you can store that info on the original tables themselves. Then your trigger just copies that info into the audit table. This is also nice because if you only need to know who the last person to touch something was you don't have to look in the audit table at all, just check that one column.

Consider this, if you don't actually delete records but add a field to mark them as deleted, you can get the user from the last modified. If you want to actually delete records then, you can have a nightly job that deletes in a batch not one at time. This could even be set up to flag if too many records are being deleted and not run.
The easiest way to do this so that nothing breaks is to rename the table, add the column IsDeleted as a bit field and then create a view with the same name the table was orginally called. The view will select all the records where isdelted is null.
Don't let anyone talk you out of using triggers for this. You don't want people who are doing unauthorized changes to be able to escape the auditing. With a trigger (and no rights to anyone except a production dba to alter the table in any way), then no one except the dba can delete without being audited. In a typical system with no stored procedures to limit direct table access, all too many people can usually directly affect a table opening it wide for fraud. People committing fraud do not typically use the application they are supposed to use to change the data. You must protect data at the database level.
When you write your triggers make sure they can handle multi-row inserts/updates/deletes. Triggers operate on the whole set of data not one row at a time.

As roufamatic said, you can either set up triggers specific to each action or you can check against the INSERTED and DELETED tables.
As for the deleting user, it is possible to pass that information into the trigger as long as the code in your application handles it. I encountered this requirement about a year ago with a client and the solution that I came up with was to use SET CONTEXT_INFO and CONTEXT_INFO() to pass the user name along. All of our database access was through stored procedures, so I just needed to add a line or two of code to the delete stored procedures to SET CONTEXT_INFO then I changed the delete triggers to get the user from CONTEXT_INFO(). The user name had to be passed as a parameter from the application of course. If you aren't using stored procedures you might be able to just do the SET CONTEXT_INFO in the application. I don't know how connection pooling might affect that method. Obviously, if someone does a delete outside of the application there wouldn't be a record of that unless you also separately captured the USERNAME() in your trigger (probably a good idea, although it wasn't necessary for our audit log, which was more for reporting than security).
There was a little bit of trickiness because CONTEXT_INFO is a binary string, but it didn't take long to get that all sorted out.
I'm afraid that I don't have any of the code handy since it was for a past client. If you run into any problems after going through the help for CONTEXT_INFO and SET CONTEXT_INFO then feel free to post here and I'll see what I can remember.

To find out what action is being taken you can use the INSERTED and DELETED tables to compare before and after values. There is no magic way to tell which user of a web app has made a change. The usual method is to have a modified column in your table and have the web app code populated this with the relevant username

Related

SQL Server Auditing Alternatives with Application User Tracking

I'm looking for an auditing solution that does exactly what Change Data Capture (CDC) does, except I need it to also track the application user that made the change. I'm currently using SQL Server 2012 Enterprise and may be upgrading to 2014 later this year.
We already have an auditing solution in place that leverages Delete, Insert, and Update triggers, but some new requirements might force us to update every audit trigger and corresponding audit table. Given various problems we've run in to with that solution over the years, this seems like as good a time as any to reevaluate and potentially replace the solution.
To give you an idea of what I'm currently working with (and may be able to leverage), we use a stored procedure (ConnectionInitialize) to store a user id with a SPID in a table (ApplicationUser) and then we delete the row using another stored procedure (ConnectionReset) once we're done making our deletes, inserts, and updates.
Were we to use CDC, I looked into adding a trigger to something like the cdc.lsn_time_mapping table, but I couldn't find a way to map the LSN back to the SPID (and therefore the user id) that was being used. This also presented some other issues in that CDC is always a little bit behind.
I looked into SQL Server Audit a little bit, but that presented some challenges of its own. We're using Transparent Data Encryption (TDE) to appease some of our security requirements, but SQL Server Audit looks like it'd need a separate encryption strategy; that and I'm more interested in the columns than in the actual SQL statements. Even so, these aren't deal-breakers for me, so I'm still looking into it.
Given what I'm trying to accomplish, does anyone have any feedback or recommendations?
By itself, CDC doesn't meet the requirements. The reason being is that CDC only grabs changes to your data, not any underlying context under which those changes were made. You can, however, get what you're looking for if you're willing to tag your data with some audit columns. The basic idea is that you append a column to your table (or to a different table if you aren't able to modify the actual table for whatever reason) and populate it with the user who last modified the record (pretty simple to do via an insert/update trigger). Once that is actual data, you can consume it however you need to (CDC being one possible mechanism).
Late answer but hopefully useful.
There is a third party tool, ApexSQL Audit, capable of meeting your requirements. My previous company is using it for years and they have been satisfied with it.
There is a helpful comparison article you can read to find more details about audited data, auditing mechanisms, integrity protection etc, for both CDC & Audit tool at one place.

saving track of changes made by users in a Multi-user sql database

I'm working on a design of a relational database. It has several tables and there are multiple users on application level. I need to know that changes to a certain record of a certain table are made, by which user, which time, and what has actually changed. There is a table for saving the user's information and this table is also included in this behavior.
How should I do this in the SQL database design so I can let users see which one of them made these changes?
What you want is a Wiki-like versioning. Basically, for every table you want to keep versions, you'll want to create at least a copy of that table with the fields you mentioned added (userid, when it was added). That's probably all there is to it, as long as you only need to track changes. Then, upon an edit, you just create a backup of the current row in that copied table and put the new one in the actual table. This way you can (hopefully) add the versioning without having to touch existing presentational code.
It gets a little more tricky, if you need to record additional actions like creation of new rows and deletion.
If you need a code example, just have look under the hood of some Wiki like https://mediawiki.org/
For starters you can look at sql server version tracking mechanisms (row versioning or row changes). After that you can look at sql server audit features. I think sql server audit would be the best for your needs.
On the other hand, if you want to make ad-hok versioning then YOU MUST NOT go to triggers. Imagine, you must create triggers for all tables for inserts, updates and deletes. This IS bad practice.
I think ad-hoc versioning should be avoided (degradation in performance and difficult to support) but in case it cannot be avoided, I would surely use CONTEXT_INFO in order to track current user and then I would try to create something that would read the schema of the table, I would get changes by using sql server change tracking mechanisms and store that in a tablename, changeduser, changedtime, column, prevValue, newValue style. I would not replicate each and every table for the changes.

What is the best way to log all user request operations: (inserts, updates, deletes in Sql Server 2008?

I have a database with 50 tables and I want to log users requests, such as inserts, updates or deletes on all the tables in the database. I can also create a trigger for this for each request type.
What is the best way to do this from a performance perspective or is there a better way to track this?
You can also create audit tables which are populated by triggers (and which allow much more flexibility than change data capture). The critical component is to capture sets of data not try to work row-by-row. It does add some overhead yes, but if you write the triggers correctly, it isn't that much. Be sure to capture who (including which application if you have multiple applications hitting the database) and when as well as the old and new values. Set up one audit table per table you want audited (too much locking if you use only one audit table). And at the time you set up your system, write the code to get data back from a bad transaction or set of transactions. That makes it easier to recover when you do have something go wrong and you need to revert. We use two tables per table audited, one contains the info about the process that did the changes (name of the application, date, user, etc. and an auditid), the other contains the details about what was changed (old and new values, ID of the record being affected and column affected). Our structure enables us to use the same structure for each table being audited, and allows the tables to change without having to change the audit table and allows us to easily script the audit tables for a new tables. It is also easy for us to see what records were changed at the same time or in the same process or to find out which of the many applications which touch our database was responsible for the bad data as well as telling us who in particular was responsible for the bad data. This helps us track down application bugs and find out why the data was changed the way it was in some cases. It also makes it easier for us to track down all the data that was affected by a broken process rather than just the one we knew about.
If you have Enterprise Edition, look into Change Data Capture. If you don't have Enterprise and aren't interested in capturing the historical values of the columns that change, look into Change Tracking.
See Comparing Change Data Capture and Change Tracking to understand the differences between the two.
Assuming all requests to insert, update and/or delete data goes through some middle-tier data access layer, I would suggest you do your logging there. This is where we do all of ours. It is much simpler than trying to extract the actual insert / delete / update statements out of SQL Server.
If you want to do auditing of data, you can look into Change Data Capture (CDC). But this requires the Enterprise Edition.

In Oracle: how can I tell if an SQL query will cause changes without executing it?

I've got a string containing an SQL statement. I want to find out whether the query will modify data or database structure, or if it will only read data. Is there some way to do this?
More info: In our application we need to let the users enter SQL-queries, mainly as part of the applications report system. These SQL queries should be allowed to read whatever they like from the databse, but they shouldn't be allowed to modify anything. No updates, deletes insert, table drops, constraint removals etc.
As of now I only test whether the first word in the string is "select", but this is too constricting and too insecure.
You should grant only select privileges on your tables for the login used by the application to be sure.
Create a new user for that part of the application that only has select privileges. Bear in mind that you'll also need to create synonyms for all the tables/views that that "read-only" user will be able to view.
The "regular" part of your application will still be able to do other operations (insert, update, delete). Just the reporting will use the read-only user.
As Horacio suggests, it is also a good idea/practice to add "wrapper" views that only expose what you want to expose. Some sort of "public API". This can give you flexibility if you need to change the underlying tables and don't want to/can't change the reports to the new definitions of said tables. This might, however, be seen as a lot of "extra work".
I agree with others that the right thing to do is use a separate schema with limited access & privileges for those queries that should be read-only.
Another option, however, is to set the transaction read-only before executing the statement entered by the user (SET TRANSACTION READ ONLY).
Create VIEWS to expose the data to end users, this is worthy because of three things:
The end user doesn't know how really your database look like.
You may can provide a simpler way to extract some pieces of data.
You can create the view with a read-only constraint:
CREATE VIEW items (name, price, tax)
AS SELECT name, price, tax_rate
FROM item
WITH READ ONLY;
Something that has worked well for me in the past, but may not fit your situation:
Use stored procedures to implement an API for the application. All modifications are done via that API. The procedures exposed to the front end are all complete units of work, and those procedures are responsible for rights enforcement.
The users running the front end application are only allowed to call the API stored procedures and read data.
Since the exposed API does complete units of work that correspond to actions the user could take via the GUI, letting them run the procedures directly doesn't get them any additional ability, nor allow them to corrupt the database accidently.
SELECT * FROM table FOR UPDATE works even with only SELECT privilege, and can still cause a lot of damage. If you want to be safe, the read only transactions are better.

How should i work in this scenario . Should I use Trigger or Leave on User to manage

I am creating an application in which i am using stored procedure where i m implementing my logic.
Now what i need to know is that- i want my database not to contain any invalid entry, for this purpose should i create triggers, for implementing my data validation logic such that when FailPasswordAttemptCount is changed to some value then should i make changes in corresponding column IsLocked accordingly thru triggers or leave it on dba to manage.
eg
if FailPassowrdAttemptCount > 3
IsCaptchaActivated=True
if FailPasswordAttemptCount>6
IsLocked=true
now if a dba changes the value of FailPasswordAttemptCount to 4 without changing IsCaptchaActivated to true then this will make an invalid entry for my frontend.
SO should i manage it thru triggers or should i left it over dba to make correct entry.
Although this type of entry is not possible thru frontend but in case any1 having privilages to access database, changes directly thru database.
For this should i leave it on user or should i manage thru triggers.
I want to make my database to remain consistent in all circumstances.
I'd make the following:
Put the data validation logic into a stored procedure
Made the stored procedure the only way the application interacts with the table
Put the code you want into the stored procedure.
Trigger-based programming paradigma grows too hard to code and maintain as the business logic complexity of your application increases.
However, if you are absolutely sure you will only have the simple logic like this, it is OK to put it into a trigger since this will require minimal changes in the ways the application interacts with the database.
I wouldn't use a trigger for something like this. Triggers are obscure and can be hard to debug for the developer. Use your tables and stored procedures to deal with the issue. Use triggers for when you don't have an alternative.
I would use a combination of both. I will try to restrict data as far as possible. And will fire trigger, so that no one can insert any invalid entry.
For this type of situation, I would probably not use a trigger, just for the situation you describe. Though I would wonder why you have dba's manually altering data in a field that closely tied to the security of your app.
I would implement this in the application logic. When calling the login sproc you can return both whether it succeeded as well as the number of failed password attempts and if captcha is needed. Regardless of if the DBA changes the 3 to a 4, your code will see the 4, ignore the result of the validation and present the user with a captcha. If you're worried about DBA's modifying the code directly you can also check the APP_NAME() function/variable to see what program is trying to modify the data. Its something to be very careful with but so is DBAs modifying fields directly.