Add Shadow Column to Insert Statement - sql-server

Situation:
I developed a Database that heavily depends on a lot of triggers to ensure data consistency (To fulfill the CFR 21 Part 11).
There are 2 tables to store users: Login & User (in favor of the Sql servers User management). Users contains static login data for External authentication and X1 are containing login data for our system (Fake User). A person have to login into our application using one of the Login data (Real User). When the system must communicate with an external system (Active Directory or FileSystem or Database) the corresponding User(Fake User) will be impersonated.
For Clarification will use X1(Real User) for Login and X2(Fake User) for User.
When the X2 is connected to the database the trigger does not know who the X1 is.
Problem:
The Trigger will log each action done by the X2 and write it the Log. As the server only knows that X2 is connected to the database it is not aware of the Real user X1.
The Customer whats to also see who the RealUser X1 was. As this information is lost when connected to the Database i have this Problem now.
Solution Attempts:
Create a StoredProcedure that takes the RealUser x1 as an Argument. This is not possible as old software relies on the current to Insert and Update the Table.
Add Additional Data the Insert/Update statement (is that possible i did not found anything related to that). The data will only used by the Trigger and should no be written to the Table.
Add the info about the current X1 user to the Connection or to a general Connection based store (does something like this exist in SqlServer?)
Use the ApplicationName property for "faking" the Username. But this makes me feel like wearing a monocle. There must be a better way
Thanks in Advance

You can use CONTEXT_INFO to associate extra data with the session that can be accessed in the trigger (like, say, the original user identity):
DECLARE #UID VARBINARY(128) = CONVERT(VARBINARY(128), N'User1')
SET CONTEXT_INFO #UID
-- Later, at the Hall of Justice
SELECT CONVERT(NVARCHAR(64), CONTEXT_INFO())
Of course, you could use a different way of identifying the user than a Unicode string, as long as it eventually fits in a VARBINARY(128).
For this to work you must consistently set the information before executing any command (the context info will be reset when the connection is closed). There are also security concerns -- SET CONTEXT_INFO is not a privileged statement and could be executed by anyone, so you must be sure you control all code in the database lest the audit information is falsified.

Related

SQL Server trigger in database a should input row in database b with different user

We have a system that stores tens of thousands of databases, each holding the data of a customer subscribing to our services. Each of those databases has their own user that is known and used by the custom thorough the distributed, thick, application front-ends we provide.
Now I would like to add a trigger to one of the tables in each one of those dbs, that should update one common db with some of the data from the inserted rows. Sort of a many-to-one db scenario if you will...
This new common db can be set up pretty much how we like - as far as users/permissions and so on, and the trigger we insert in the old dbs can be written pretty freely. But we can not change those customer dbs as far as users/permissions.
Currently I'm experimenting with using the guest user, it has been given writing access, on the common db (named "foo" in example below) like this, but it is not working (i think due to the guest user of the common db not being allowed to access the customer db table - named Bf in example below - it is triggered in?). It may also be that I'm using "execute as" where I should be using execute as login="? I'm having a hard time finding a comprehensible place that describes the difference.
This is the trigger we would like to get working, and inserted in every customer db:
ALTER TRIGGER [dbo].[trgAfterInsert] ON [dbo].[Bf]
WITH EXECUTE AS 'guest'
FOR INSERT
AS
begin
insert into [foo].[dbo].[baar]
(publicKey, fileId)
SELECT b.publicKey, a.autoId
FROM client_db_1.[dbo].[Integrationer] as b, inserted as a where
a.autoId in (select top 1 autoId from inserted order by autoId)
end
As you may guess, I'm not an experienced user of triggers or sql permissions/access work. But the info we want to collect is harmless, and it should take close to 0 time to execute, and in a secured, non-exposed, environment, so I'm very willing to read/learn if anyone has advice?
/Henry

Find the source that fired a query

This is a hypothetical question - the problem listed below is entirely fictional, but I believe if anyone has an answer it could prove useful for future reference.
We have a situation wherein multiple systems all populate the same data table on our SQL Server. One of these systems seems to be populating the table incorrectly, albeit in a consistent pattern (leading me to believe it is only a bug in a single system, not multiple) These are majoritively third-party systems and we do not have access to modify or view their source code, nor alter their functionality. We want to file a bug report with the culprit system's developer, but we don't know which one it is as the systems leave no identifiable trace on the table - those in charge before me, when the database was new and only occasionally used by a single system, believed that a single timestamp field was an adequate audit, and this has never been reconsidered.
Our solution has to be entirely SQL-based. Our thought was to write a trigger on the table, and somehow pull through the source of the query - ie, where it came from - but we don't know how, or even if that's possible.
There are some clear solutions to this - for eg contact all the developers to update their software to populate a new software_ID field, and then use the new information to identify the faulty system later (and save my fictional self similar headaches later) - but I'm particularly interested to know if there's anything that could be done purely in-house on SQL Server (or another clever solution) with the restrictions noted.
you can use functions:
select HOST_NAME(), APP_NAME()
So you will know the computer and application that caused the changes..
And you can modify application connection string to add custom Application name, for example:
„Data Source=SQLServerExpress;Initial Catalog=TestDB;
Integrated Security=True; Application Name=MyProgramm”
You could create a copy of the table in question with one additional nvarchar field to hold the identifier.
Then create a trigger for insert (and maybe update) on the table, and in the trigger insert the same rows to the copy, adding in an identifier. The identifier could be for instance the login name on the connection:
insert into tableCopy select SUSER_SNAME(), inserted.* from inserted
or maybe a client IP:
declare #clientIp varchar(255);
SELECT clientIp = client_net_address
FROM sys.dm_exec_connections
WHERE Session_id = ##SPID
insert into tableCopy select #clientIp, inserted.* from inserted
or possibly something else that you could get from the connection context (for lack of a more precise term) that can identify the client application.
Make sure though that inserting into the table copy will under no circumstances cause errors. Primary keys and indexes should probably be dropped from the copy.
Just an idea: create a trigger that save in a dedicated table the info obtained by EXEC sp_who2 when suspicious value are stored in the table.
Maybe you can filter sp_who2 values by status RUNNABLE.
So, if multiple users share the same login, you can determine the exact moment in which the command is executed and start your research from this...

Return value for correct session?

I'm working on a project in dead ASP (I know :( )
Anyway it is working with a kdb+ database which is major overkill but not my call. Therefore to do inserts etc we're having to write special functions so they can be handled.
Anyway we've hit a theoretical problem and I'm a bit unsure how it should be dealt with in this case.
So basically you register a company, when you submit validation will occur and the page will be processed, inserting new values to the appropriate tables. Now at this stage I want to pull ID's from the tables and use them in the session for further registration screens. The user will never add a specific ID of course so it needs to be pulled from the database.
But how can this be done? I'm particularly concerned with 2 user's simultaneously registering, how can I ensure the correct ID is passed back to the correct session?
Thank you for any help you can provide.
Instead of having the ID set at the point of insert, is it possible for you to "grab" an ID value before hand, and then use that value throughout the process?
So:
Start the registration.
System connects to the database, creates an ID (perhaps from an ID table) and Stores in ASP Session.
Company registers.
You validate and insert data into DB (including the ID session)
The things you put in the Session(...) collection is only visible to that session (i.e. the session is used only by the browser windows on one computer). The session is identified by a GUID value that is stored in a cookie on the client machine. It is "safe" to store your IDs there (other users won't be able to read them easily) .
either your id can include date and time - so it will be example - id31032012200312 - but if you still think that 2 people can register at the same type then I would use recordset locks liek the ones here - http://www.w3schools.com/ado/prop_rs_locktype.asp
To crea ids like above in asp you do - replace(date(),"/","") ' and then same with time with ":"
Thanks

If my database user is read only, why do I need to worry about sql injection?

Can they (malicious users) describe tables and get vital information? What about if I lock down the user to specific tables? I'm not saying I want sql injection, but I wonder about old code we have that is susceptible but the db user is locked down. Thank you.
EDIT: I understand what you are saying but if I have no response.write for the other data, how can they see it. The bringing to crawl and dos make sense, so do the others but how would they actually see the data?
Someone could inject SQL to cause an authorization check to return the equivalent of true instead of false to get access to things that should be off-limits.
Or they could inject a join of a catalog table to itself 20 or 30 times to bring database performance to a crawl.
Or they could call a stored procedure that runs as a different database user that does modify data.
'); SELECT * FROM Users
Yes, you should lock them down to only the data (tables/views) they should actually be able to see, especially if it's publicly facing.
Only if you don't mind arbitrary users reading the entire database. For example, here's a simple, injectable login sequence:
select * from UserTable where userID = 'txtUserName.Text' and password = 'txtPassword.Text'
if(RowCount > 0) {
// Logged in
}
I just have to log in with any username and password ' or 1 = 1 to log in as that user.
Be very careful. I am assuming that you have removed drop table, alter table, create table, and truncate table, right?
Basically, with good SQL Injection, you should be able to change anything that is dependent on the database. This could be authorization, permissions, access to external systems, ...
Do you ever write data to disk that was retrieved from the database? In that case, they could upload an executable like perl and a perl file and then execute them to gain better access to your box.
You can also determine what the data is by leveraging a situation where a specific return value is expected. I.e. if the SQL returns true, execution continues, if not, execution stops. Then, you can use a binary search in your SQL. select count(*) where user_password > 'H'; If the count is > 0 it continues. Now, you can find the exact plain text password without requiring it to ever be printed on the screen.
Also, if your application is not hardened against SQL errors, there might be a case where they can inject an error in the SQL or in the SQL of the result and have the result display on the screen during the error handler. The first SQL statement collects a nice list of usernames and passwords. The second statement tries to leverage them in a SQL condition for which they are not appropriate. If the SQL statement is displayed in this error condition, ...
Jacob
I read this question and answers because I was in the process of creating a SQL tutorial website with a readonly user that would allow end users to run any SQL.
Obviously this is risky and I made several mistakes. Here is what I learnt in the first 24 hours (yes most of this is covered by other answers but this information is more actionable).
Do not allow access to your user table or system tables:
Postgres:
REVOKE ALL ON SCHEMA PG_CATALOG, PUBLIC, INFORMATION_SCHEMA FROM PUBLIC
Ensure your readonly user only has access to the tables you need in
the schema you want:
Postgres:
GRANT USAGE ON SCHEMA X TO READ_ONLY_USER;
GRANT SELECT ON ALL TABLES IN SCHEMA X TO READ_ONLY_USER
Configure your database to drop long running queries
Postgres:
Set statement_timeout in the PG config file
/etc/postgresql/(version)/main/postgresql.conf
Consider putting the sensitive information inside its own Schema
Postgres:
GRANT USAGE ON SCHEMA MY_SCHEMA TO READ_ONLY_USER;
GRANT SELECT ON ALL TABLES IN SCHEMA MY_SCHEMA TO READ_ONLY_USER;
ALTER USER READ_ONLY_USER SET SEARCH_PATH TO MY_SCHEMA;
Take care to lock down any stored procedures and ensure they can not be run by the read only user
Edit: Note by completely removing access to system tables you no longer allow the user to make calls like cast(). So you may want to run this again to allow access:
GRANT USAGE ON SCHEMA PG_CATALOG to READ_ONLY_USER;
Yes, continue to worry about SQL injection. Malicious SQL statements are not just about writes.
Imagine as well if there were Linked Servers or the query was written to access cross-db resources. i.e.
SELECT * from someServer.somePayrollDB.dbo.EmployeeSalary;
There was an Oracle bug that allowed you to crash the instance by calling a public (but undocumented) method with bad parameters.

How to track data changes in a database table

What is the best way to track changes in a database table?
Imagine you got an application in which users (in the context of the application not DB users ) are able to change data which are store in some database table. What's the best way to track a history of all changes, so that you can show which user at what time change which data how?
In general, if your application is structured into layers, have the data access tier call a stored procedure on your database server to write a log of the database changes.
In languages that support such a thing aspect-oriented programming can be a good technique to use for this kind of application. Auditing database table changes is the kind of operation that you'll typically want to log for all operations, so AOP can work very nicely.
Bear in mind that logging database changes will create lots of data and will slow the system down. It may be sensible to use a message-queue solution and a separate database to perform the audit log, depending on the size of the application.
It's also perfectly feasible to use stored procedures to handle this, although there may be a bit of work involved passing user credentials through to the database itself.
You've got a few issues here that don't relate well to each other.
At the basic database level you can track changes by having a separate table that gets an entry added to it via triggers on INSERT/UPDATE/DELETE statements. Thats the general way of tracking changes to a database table.
The other thing you want is to know which user made the change. Generally your triggers wouldn't know this. I'm assuming that if you want to know which user changed a piece of data then its possible that multiple users could change the same data.
There is no right way to do this, you'll probably want to have a separate table that your application code will insert a record into whenever a user updates some data in the other table, including user, timestamp and id of the changed record.
Make sure to use a transaction so you don't end up with cases where update gets done without the insert, or if you do the opposite order you don't end up with insert without the update.
One method I've seen quite often is to have audit tables. Then you can show just what's changed, what's changed and what it changed from, or whatever you heart desires :) Then you could write up a trigger to do the actual logging. Not too painful if done properly...
No matter how you do it, though, it kind of depends on how your users connect to the database. Are they using a single application user via a security context within the app, are they connecting using their own accounts on the domain, or does the app just have everyone connecting with a generic sql-account?
If you aren't able to get the user info from the database connection, it's a little more of a pain. And then you might look at doing the logging within the app, so if you have a process called "CreateOrder" or whatever, you can log to the Order_Audit table or whatever.
Doing it all within the app opens yourself up a little more to changes made from outside of the app, but if you have multiple apps all using the same data and you just wanted to see what changes were made by yours, maybe that's what you wanted... <shrug>
Good luck to you, though!
--Kevin
In researching this same question, I found a discussion here very useful. It suggests having a parallel table set for tracking changes, where each change-tracking table has the same columns as what it's tracking, plus columns for who changed it, when, and if it's been deleted. (It should be possible to generate the schema for this more-or-less automatically by using a regexed-up version of your pre-existing scripts.)
Suppose I have a Person Table with 10 columns which include PersonSid and UpdateDate. Now, I want to keep track of any updates in Person Table.
Here is the simple technique I used:
Create a person_log table
create table person_log(date datetime2, sid int);
Create a trigger on Person table that will insert a row into person_log table whenever Person table gets updated:
create trigger tr on dbo.Person
for update
as
insert into person_log(date, sid) select updatedDTTM, PersonSID from inserted
After any updates, query person_log table and you will be able to see personSid that got updated.
Same you can do for Insert, delete.
Above example is for SQL, let me know in case of any queries or use this link :
https://web.archive.org/web/20211020134839/https://www.4guysfromrolla.com/webtech/042507-1.shtml
A trace log in a separate table (with an ID column, possibly with timestamps)?
Are you going to want to undo the changes as well - perhaps pre-create the undo statement (a DELETE for every INSERT, an (un-) UPDATE for every normal UPDATE) and save that in the trace?
Let's try with this open source component:
https://tabledependency.codeplex.com/
TableDependency is a generic C# component used to receive notifications when the content of a specified database table change.
If all changes from php. You may use class to log evry INSERT/UPDATE/DELETE before query. It will be save action, table, column, newValue, oldValue, date, system(if need), ip, UserAgent, clumnReference, operatorReference, valueReference. All tables/columns/actions that need to log are configurable.

Resources