Block access to column without changing query - sql-server

I have a Microsoft SQL Server with data that needs to be protected (certain sensitive columns of some tables) and an application that queries that database like this:
SELECT BoringColumn, SensitiveColumn FROM Table
I have the following restrictions:
I have multiple users (3-4) each with different columns visible or not.
In this example SensitiveColumn should not be accessible.
I can not directly update the queries that the application sends
What did I try already:
I tried to use SQL Servers Dynamic Data Masking feature. However it's not granular enough, you can just turn it on or off per user but not just for some columns. And its can leak data in queries, the link above explains that as well.
I know I can just deny the user SELECT on Table.SensitiveColumn.
However then any existing query asking for the table just breaks with permission errors.
What other options do I have left?
Ideally I would like something that replaces the query on the serverside and executes something like this:
SELECT BoringColumn, 'N/A' as SensitiveColumn FROM Table

I think I found a possible solution:
Change the table structure - Rename the SensitiveColumn to a different name, and add a computed column with the old name of the SensitiveColumn, that will show results based on current_user.
CREATE TABLE tblTest
(
boringColumn int,
SensitiveBase varchar(10), -- no user should have direct access to this column!
SensitiveColumn as
case current_user
when 'Trusted login' then SensitiveBase
else 'N/A'
end
)
The one thing I'm not sure about is if you can deny access to the SensitiveBase column but grant it to the SensitiveColumn.
I'll leave you to test it yourself.
If that can't be done, you can simply grant select permissions on the SensitiveBase column only to trusted login and deny them for everyone else.

Related

SQLServer, temporarily disabling row level security

I have implemented row level security in SQLServer in Person table (to meet GDPR requirements) so that a basic user can see only subset of personal records. The
Person table has some data (RFID tag) that must be unique in the system. So my app checks that there is no duplicate RFIDtag.
How to do this check when RLS is on, because the query only sees a subset of rows, but the RFID must be globally unique ? What could be the best way to run this query with temporarily disabling RLS ?
My first idea is to use a stored function, to perform the check. The function executed as 'sa' user sa could see all rows. Any other (simpler) ideas ?
The UNIQUE index is the safest approach, like said in #Jeroen's comment.
Disabling row level security can be needed in other contexts, though. When needed, my suggestion is:
Create an 'admin' user just for this (CREATE USER admin WITHOUT LOGIN;)
Include logic in your validation function to account for this user:
CREATE FUNCTION Security.fn_securitypredicate(#param as VARCHAR(100))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS Result
WHERE
/* Your logic here */
OR
USER_NAME() = 'admin' ;
This way, when you are impersonating 'admin' user all records are accessible. Any other user, your row level security logic still applies.
To impersonate the user when running a query:
EXECUTE AS USER = 'admin';
SELECT * FROM MyTable;
REVERT; -- Returns to the original user context

Dynamic Row Level Security In a SQL Server Database Using Extended Properties

We have a requirement to provide customer access to a staging database so that they can extract their data into their own servers, but every table contains all customers data. All of the tables have a 'CustomerID' column. Customers should only see rows where the customerID is the same as theirs.
I am not looking for suggestions to create separate databases or views for each customer as both suggestions are high maintenance and low efficiency.
My solution has to work with:
100GB database
400 Tables
Updates every 30 minutes from the core transaction database
Quarterly schema changes (Application is in continuous Development).
Can anyone give me a definitive answer as to why the following method is not secure or will not work?:
I've set up a database user for each customer, with their customerID as an extended property.
I've created a view of every table that dynamically selects * from the table where the customerID column is the same as the extended property CustomerID of the logged in user. The code looks like this and appears to work well:
CREATE VIEW [CustomerAccessDatabase].[vw_Sales]
AS SELECT * FROM [CustomerAccessDatabase].[Sales]
WHERE [Sales].[CustomerID]=
(SELECT CONVERT(INT,p.value) AS [Value]
FROM sys.extended_properties
JOIN sys.sysusers ON extended_properties.major_id=sysusers.[uid]
AND extended_properties.name = 'CustomerID'
AND sysusers.[SID]=(SELECT suser_sid())
);
GO
To provide access to the views I've created a generic database role 'Customer_Access_Role'. This role has access granted to all of the table views, but access to the database tables themselves is denied.
To prevent users from changing their own customerID I've denied access to the extended properties like so:
USE [master];
GO
DENY EXEC ON sys.sp_addextendedproperty to [public];
GO
DENY EXEC ON sys.sp_dropextendedproperty to [public];
GO
DENY EXEC ON sys.sp_updateextendedproperty to [public];
GO
The end result is that I only need one database, and one set of permissions.
To add a new customer all I would need to do is create a new user with their customerID as an extended attribute and add them to the Customer_Access_Role. Thats it!
I am going to reiterate what everyone is stating already and sum it up.
You are making your job harder than it has to be.
Create a View, that is just their data and then give them Security access to that View.
Alternatively, extract all their data out of the "Core" database and into their own and give them the necessary access to that data.

prevent some user from seeing db2 tables structure

How can I restrict some users in DB2, not to see the table structure. I set the user privilege and restrict user from table access. so that user can not select data or change table but still can see the table structure or describe it.
This problem refers to row access in tables which is added in db2 version 10.
I had this problem too.
you can use this version - if applicable- and restrict user access from specific table structures.
You need to remove the select grant on catalog tables. For example, the following query should return 0 rows when executing with q restricted user.
db2 "select tabschema, tabname from syscat.tables"
All tables and views in the following schemas should not have select on public, nor in any group the restrictive user is in.
sysibm
syscat
db2 revoke select on SYSIBM.SYSTABLES from username

How to merge table from access to SQL Express?

I have one table named "Staff" in access and also have this table(same name) in SQL 2008.
Both table have thousands of records. I want to merge records from the access table to sql table without affecting the existing records in sql. Normally, I just export using OCBC driver and that works fine if that table doesn't exist in sql server. Please advise. Thanks.
A simple append query from the local access table to the linked sql server table should work just fine in this case.
So, just drop in the first (from) table into the query builder. Then change the query type to append, and you are prompted for the append table name.
From that point on, just drop in the columns you want (do not drop in the PK column, as they need not be used nor transferred in this case).
You can also type in the sql directly in the query builder. Either way, you will wind up with something like:
INSERT INTO dbo_custsql
( ADMINID, Amount, Notes, Status )
SELECT ADMINID, Amount, Notes, Status
FROM custsql1;
This may help: http://www.red-gate.com/products/sql-development/sql-compare/
Or you could write a simple program to read from each data set and do the comparison, adding, updating, and deleting, etc.

Triggers in sql server 2008 management studio

I am trying to set up the trigger in a way that when the administrator (not users) make any changes to the database, all the changed data with the administrator name and time gets saved in the audit table (already created) that has all the possible fields.
I have created triggers on each table for any sort of updates in those tables. The trigger saves the information in audittable. However, i want to restrict this action for administrators only. Like I only want to keep the record of changes made by adminsitrators with their name, time and changes made by them(I have a separate table for adminsitrator names, username, pw and all that).
Can someone please help me with that.
Thanks
To get the user you may use:
server level (login)
select system_user , suser_sname() , suser_sid()
db level (db user)
select session_user , current_user , user , user_name() , user_id()
Than and check that this user is admin or not in that additional table.
You can try one of these two functions, depending on what you define as "administrator".
SELECT IS_MEMBER('dbo'), IS_SRVROLEMEMBER('sysadmin')
The IS_MEMBER function evaluates the database role and the IS_SRVROLEMEMBER evaluates the server role. So, if you want to know if the user is a database admin, you would use IS_MEMBER. These will work for user-defined roles as well as built-in roles.
UPDATE:
Here's an example of the trigger that would add data to the audit table when a server administrator inserts data to the table.
CREATE TRIGGER trg_InfoUpdates ON tblCustomerInfo
FOR INSERT AS
IF IS_SRVROLEMEMBER('sysadmin') = 1
BEGIN
INSERT INTO tblAuditLog (CustomerID)
SELECT CustomerID
FROM inserted
END
;

Resources