Skip to content

Dynamic Data Masking in SQL Server

  • by
Hello and welcome back to another post!

I’m not sure about you guys, but in different jobs I’ve had, there’s always people querying the production database, accessing to sensitive information that they should not be allowed to see like phone numbers, social security numbers, accounts and more.

As DBAs and developers, we have to understand that security is the most important regarding the data. However, there’s going to be people that *need* to be always looking at some data. They could be Data Analysts, people in Finance department, somebody from HR, and others… sometimes even end users and customers have access to that data (ugh!).

SECURITY MECHANISMS

There are different ways how to deal with this and avoid everybody to be looking at things they shouldn’t.

One thing we can do is,  to create roles and give permissions to see just the tables or views they need. Views are another mechanism we can use. Here, we just create the view with the data from the different tables they need and grant their user, or role the permission to see the view.

Stored procedures can also be used for this the same way we do with views. The catch is that, we need to GRANT EXECUTE to the user, role or group, otherwise it will fail and you’ll be getting lots of emails of people complaining.

There is a different way to do this in case you don’t want to restrict people from seeing all the objects in the database (of course, you grant SELECT permission only). SQL Server 2016 introduced some new security features, and one of this is what I’ll show you next.

DYNAMIC DATA MASKING

By implementing Dynamic Data Masking (DDM), the DBA defines column level data masking preventing users from reading sensitive data. This allows the people to query the database themselves without seeing the sensitive content of columns. DDM works at query runtime, it doesn’t affect the stored data of the table.

DDM works by defining a fixed set of functions to the columns we want to hide the data from. So, every time we query a table with a DDM function defined it’ll check if we have permission to see the contents of the columns or if it will be displayed as we defined in the masking function.

These are the four masking functions that we can use:
FunctionDescription
defaultImplements full masking.
emailMasks email addresses as bXXXX@XXXX.com
randomMasks the original numeric value with a random value from a range.
partialUses a custom string to mask characters in the data.
Let’s see and example of how we can implement DDM and how it looks like. First, create two users for the test.
CREATE USER DataAnalyst WITHOUT LOGIN;
CREATE USER FinanceGuy	WITHOUT LOGIN;
Now, we’re going to create a table and define the functions we want to use to mask the data.
CREATE TABLE dbo.SuperSensitiveCustomerData
(
	ID INT IDENTITY(1,1) PRIMARY KEY
,	FirstName VARCHAR(100)
,	LastName VARCHAR(100) MASKED WITH(FUNCTION = 'partial(1,"xxxx", 0)')
,	HomeAddress VARCHAR(250) MASKED WITH(FUNCTION = 'default()')
,	SSN VARCHAR(15) MASKED WITH(FUNCTION = 'partial(6, "xxxx", 0)')
,	Email VARCHAR(50) MASKED WITH(FUNCTION = 'email()')
,	CreditCard VARCHAR(40) MASKED WITH(FUNCTION = 'partial(12,"xxxx", 0)')
,	DateOfBirth DATE MASKED WITH(FUNCTION = 'default()')
)

See how I’m using different functions like partial(), default() and email(). In this case I didn’t use the random() function.

OK, now that we have our table, we need to populate it with some data. I went to Mockaroo which is a service that generates random test data with different formats like SSNs, Dates, Credit Card numbers and so on. I’ll include the data I used on my github along with the code in this post.

We already have our table and data, we need to grant SELECT permission to our users. And then, execute the query as if we are the users.

GRANT SELECT ON dbo.SuperSensitiveCustomerData TO DataAnalyst, FinanceGuy;

EXECUTE(N'SELECT * FROM dbo.SuperSensitiveCustomerData') AS USER = N'DataAnalyst'
EXECUTE(N'SELECT * FROM dbo.SuperSensitiveCustomerData') AS USER = N'FinanceGuy'
And this is how they will see the data:

As you can see, none of them have the UNMASK permission, so they are not able to see the whole contents of the columns. They see just part of the information there. I’m going give DataAnalyst user permission to see the data.

GRANT UNMASK TO DataAnalyst;

EXECUTE(N'SELECT * FROM dbo.SuperSensitiveCustomerData') AS USER = N'DataAnalyst'
EXECUTE(N'SELECT * FROM dbo.SuperSensitiveCustomerData') AS USER = N'FinanceGuy'
Now, this is what each of them see: DataAnalyst user is the one on top, see the user is now able to see everything while FinanceGuy doesn’t.

BUT MY TABLE ALREADY EXISTS… WHAT ABOUT THAT?

If you already have a table, and want to implement DDM so unauthorized users won’t see sensitive data, you can alter the table and alter each column adding the function of your choice.

I created a copy of the table we were using but now with no masking functions defined.
-- Create a new table as a copy of the first, but with no masking functions.
CREATE TABLE dbo.MoreSuperSensitiveCustomerData
(
    ID		INT IDENTITY(1,1) PRIMARY KEY
,	FirstName	VARCHAR(100)
,	LastName	VARCHAR(100)		
,	HomeAddress VARCHAR(250)	
,	SSN		VARCHAR(15)				
,	Email		VARCHAR(50)			
,	CreditCard	VARCHAR(40)		
,	DateOfBirth DATE			
);

-- Copy the data from our first table
INSERT INTO MoreSuperSensitiveCustomerData(FirstName, LastName, HomeAddress, SSN, Email, CreditCard, DateOfBirth)
SELECT FirstName, LastName, HomeAddress, SSN, Email, CreditCard, DateOfBirth FROM SuperSensitiveCustomerData;

-- Grant select to the new table
GRANT SELECT ON dbo.MoreSuperSensitiveCustomerData TO DataAnalyst, FinanceGuy;

-- Query as the users
EXECUTE(N'SELECT * FROM dbo.MoreSuperSensitiveCustomerData') AS USER = N'DataAnalyst';
EXECUTE(N'SELECT * FROM dbo.MoreSuperSensitiveCustomerData') AS USER = N'FinanceGuy';
And, they both can see the data without masking. Now, I need to do an ALTER TABLE and ALTER COLUMN command to define the masking function to the columns I want to hide.
ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN LastName ADD MASKED WITH(FUNCTION = 'partial(1,"xxxx", 0)');

ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN HomeAddress ADD MASKED WITH(FUNCTION = 'default()');

ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN SSN ADD MASKED WITH(FUNCTION = 'partial(6, "xxxx", 0)');

ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN Email ADD MASKED WITH(FUNCTION = 'email()');

ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN CreditCard ADD MASKED WITH(FUNCTION = 'partial(12,"xxxx", 0)');

ALTER TABLE dbo.MoreSuperSensitiveCustomerData
ALTER COLUMN DateOfBirth ADD MASKED WITH(FUNCTION = 'default()');
Test one more time with the users.
EXECUTE(N'SELECT * FROM dbo.MoreSuperSensitiveCustomerData') AS USER = N'DataAnalyst';
EXECUTE(N'SELECT * FROM dbo.MoreSuperSensitiveCustomerData') AS USER = N'FinanceGuy';
If you remember, the DataAnalyst user already had the UNMASK permission so she is able to see the content of the column. There you have it.

Before I forget, there are some limitations. I guess you already figured out the first one. Yeah, the UNMASK permission has to be on database level. Another limitation is that you can’t mask the columns encrypted with the Always Encrypted feature. There are a few more, but these two are the ones I can remember now being 11:43 p.m.

I hope you liked the post and that you’ve learned something new or refreshed some old knowledge. Also, remember that you can find the code for this and other posts in the following link: https://github.com/learningsql/sql-code

If you have any questions or comments, shoot me an email, leave a message or tweet me here. Don’t forget to like and share. See you soon. Cheers! If you want to receive updates and new blog posts to your inbox, subscribe to my LearningSQL with Beno email list down here. [mc4wp_form id=”331″]

Discover more from Ben Rodríguez

Subscribe to get the latest posts sent to your email.

Discover more from Ben Rodríguez

Subscribe now to keep reading and get access to the full archive.

Continue reading