Home
%3CLINGO-SUB%20id%3D%22lingo-sub-662798%22%20slang%3D%22en-US%22%3ELesson%20Learned%20%2388%3A%20Implementing%20Data%20Masking%20with%20EXECUTE%20AS%20USER%20in%20Azure%20SQL%20Database%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-662798%22%20slang%3D%22en-US%22%3E%3CP%3EOur%20customer%20is%20using%20a%20legacy%20application%20that%20they%20cannot%20change%20the%20user%20that%20is%20connecting%20to%20the%20database%20but%20they%20want%20to%20use%20Data%20Masking%20and%20Row%20Level%20Security%20features%20in%20Azure%20SQL%20Database.%20Unfortunately%2C%20they%20cannot%20impersonate%20the%20user%20when%20they%20execute%20their%20application%2C%20for%20example%2C%20to%20use%20Azure%20Active%20Directory.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3EMy%20first%20approach%20was%3A%26nbsp%3BAdding%20a%20logic%20in%20their%20different%26nbsp%3BTSQL%20that%20they%20have%3A%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3ECreated%20an%20example%20table%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CPRE%3ECREATE%20TABLE%20TableExample%20(Id%20INTEGER%2C%20%0A%20%20%20%20%20%20UserType%20INTEGER%2C%0A%20%20%20%20%20%20DataCannotSeeUser1%20VARCHAR(100)%2C%0A%20%20%20%20%20%20DataCannotSeeUser2%20VARCHAR(100))%0A%0AINSERT%20INTO%20TableExample%20values(1%2C1%2C'Maria'%2C'Jose')%0AINSERT%20INTO%20TableExample%20values(2%2C2%2C'Jose'%2C'Maria')%0AINSERT%20INTO%20TableExample%20values(3%2C3%2C'Juan'%2C'Maria')%0AINSERT%20INTO%20TableExample%20values(3%2C2%2C'Juan'%2C'Maria')%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EOur%20customer%20retrieve%20data%20in%20two%20ways%20the%20data%2C%20using%20Stored%20Procedure%20or%20Views%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CPRE%3ECREATE%20PROCEDURE%20GiveData%20(%40UserType%20INT)%0AAS%0ABEGIN%0A%20IF(%40UserType%26gt%3B%3D1)%0A%20%20BEGIN%0A%20%20%20%20IF(%40UserType%3D1)%0A%20%20%20%20BEGIN%0A%20%20%20%20%20%20%20SELECT%20DataCannotSeeUser2%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20STUFF(DataCannotSeeUser1%2C2%2CLEN(DataCannotSeeUser1)%2CREPLICATE('*'%2CLEN(DataCannotSeeUser1)))%20AS%20DataCannotSeeUser1%20FROM%20TableExample%20WHERE%20UserType%20%3D%20%40UserType%0A%20%20%20%20END%0A%20%20%20%20IF(%40UserType%3D2)%0A%20%20%20%20BEGIN%0A%20%20%20%20%20%20%20SELECT%20DataCannotSeeUser1%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20STUFF(DataCannotSeeUser2%2C2%2CLEN(DataCannotSeeUser2)%2CREPLICATE('*'%2CLEN(DataCannotSeeUser2)))%20AS%20DataCannotSeeUser2%20FROM%20TableExample%20WHERE%20UserType%20%3D%20%40UserType%0A%20%20%20%20END%0A%20%20END%0A%20ELSE%0A%20%20%20BEGIN%0A%20%20%20%20SELECT%20DataCannotSeeUser1%2C%0A%20%20%20%20%20%20%20%20DataCannotSeeUser2%20FROM%20TableExample%0A%20%20%20END%20%0AEND%20%0A%0Acreate%20view%20ViewUser1%0AAS%0ASELECT%20DataCannotSeeUser2%2C%20%0A%20%20%20%20%20%20%20STUFF(DataCannotSeeUser1%2C2%2CLEN(DataCannotSeeUser1)%2CREPLICATE('*'%2CLEN(DataCannotSeeUser1)))%20AS%20DataCannotSeeUser1%20FROM%20TableExample%0A%0Acreate%20view%20ViewUser2%0AAS%0A%20%20%20%20%20%20%20SELECT%20DataCannotSeeUser1%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20STUFF(DataCannotSeeUser2%2C2%2CLEN(DataCannotSeeUser2)%2CREPLICATE('*'%2CLEN(DataCannotSeeUser2)))%20AS%20DataCannotSeeUser2%20FROM%20TableExample%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%3CSTRONG%3EThe%20second%20approach%20was%20using%20EXECUTE%20AS%20based%20on%2C%20for%20example%2C%20a%20variable%20session%20of%20the%20application%20and%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsql%2Frelational-databases%2Fsystem-stored-procedures%2Fsp-set-session-context-transact-sql%3Fview%3Dsql-server-2017%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%3Esession_context%3C%2FA%3E%20feature%2C%20for%20example%3A%3C%2FSTRONG%3E%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%3E%0A%3CLI%3EConnect%20to%20master%20database%20and%20create%20the%20login%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%3ECREATE%20LOGIN%20login1%20WITH%20PASSWORD%20%3D%20'ComplexPassword99!'%20%20%3C%2FPRE%3E%0A%3CUL%3E%0A%3CLI%3ECreate%20database%20user%2C%20table%20example%20and%20give%20the%20permissions%20to%20the%20table%20to%20this%20user.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%3ECREATE%20USER%20user1%20FOR%20LOGIN%20login1%3B%20%20%0A%0ACREATE%20TABLE%20Contact%0A%20%20(ID%20int%20IDENTITY%20PRIMARY%20KEY%2C%0A%20%20%20Nombre%20varchar(100)%20MASKED%20WITH%20(FUNCTION%20%3D%20'partial(1%2C%22XXXXXXX%22%2C0)')%20NULL%2C%0A%20%20%20Apellido%20varchar(100)%20NOT%20NULL%2C%0A%20%20%20NrTlf%20varchar(12)%20MASKED%20WITH%20(FUNCTION%20%3D%20'default()')%20NULL%2C%0A%20%20%20Email%20varchar(100)%20MASKED%20WITH%20(FUNCTION%20%3D%20'email()')%20NULL)%3B%0A%20%0AINSERT%20Contact%20(Nombre%2C%20Apellido%2C%20NrTlf%2C%20Email)%20VALUES%20%0A('Roberto'%2C%20'Torres'%2C%20'91551234567'%2C%20'RTorres%40contoso.com')%2C%0A('Juan'%2C%20'Galvin'%2C%20'95551234568'%2C%20'JGalvin%40contoso.com')%2C%0A('Jos%C3%A9'%2C%20'Garcia'%2C%20'95551234569'%2C%20'Jgarcia%40contoso.net')%3B%0A%0AGRANT%20SELECT%20ON%20Contact%20to%20User1%3B%3C%2FPRE%3E%0A%3CUL%3E%0A%3CLI%3EAdd%20the%20value%20of%20the%20parameter%20UserToFilter%20by%20the%20name%20of%20the%20user%2C%20after%20stablishing%20the%20connection.%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CPRE%3EEXEC%20sp_set_session_context%20'UserToFilter'%2C%20'User1'%0A%3C%2FPRE%3E%0A%3CUL%3E%0A%3CLI%3EExecute%20AS%20example%20to%20filter%20data%3A%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CPRE%3EEXECUTE%20AS%20USER%20%3D%20convert(varchar(200)%2CSESSION_CONTEXT(N'UserToFilter'))%20%0ASELECT%20*%20FROM%20Contact%3B%0AREVERT%3B%20%3C%2FPRE%3E%0A%3CP%3EEnjoy!%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-662798%22%20slang%3D%22en-US%22%3E%3CP%3EOur%20customer%20is%20using%20a%20legacy%20application%20that%20they%20cannot%20change%20the%20user%20that%20is%20connecting%20to%20the%20database%20but%20they%20want%20to%20use%20Data%20Masking%20and%20Row%20Level%20Security%20features%20in%20Azure%20SQL%20Database.%20Unfortunately%2C%20they%20cannot%20impersonate%20the%20user%20when%20they%20execute%20their%20application.%3C%2FP%3E%3C%2FLINGO-TEASER%3E

Our customer is using a legacy application that they cannot change the user that is connecting to the database but they want to use Data Masking and Row Level Security features in Azure SQL Database. Unfortunately, they cannot impersonate the user when they execute their application, for example, to use Azure Active Directory.

 

My first approach was: Adding a logic in their different TSQL that they have:

 

  • Created an example table:
CREATE TABLE TableExample (Id INTEGER, 
			   UserType INTEGER,
			   DataCannotSeeUser1 VARCHAR(100),
			   DataCannotSeeUser2 VARCHAR(100))

INSERT INTO TableExample values(1,1,'Maria','Jose')
INSERT INTO TableExample values(2,2,'Jose','Maria')
INSERT INTO TableExample values(3,3,'Juan','Maria')
INSERT INTO TableExample values(3,2,'Juan','Maria')

 

 

  • Our customer retrieve data in two ways the data, using Stored Procedure or Views:
CREATE PROCEDURE GiveData (@UserType INT)
AS
BEGIN
 IF(@UserType>=1)
  BEGIN
    IF(@UserType=1)
    BEGIN
       SELECT DataCannotSeeUser2, 
	          STUFF(DataCannotSeeUser1,2,LEN(DataCannotSeeUser1),REPLICATE('*',LEN(DataCannotSeeUser1))) AS DataCannotSeeUser1 FROM TableExample WHERE UserType = @UserType
    END
    IF(@UserType=2)
    BEGIN
       SELECT DataCannotSeeUser1, 
	          STUFF(DataCannotSeeUser2,2,LEN(DataCannotSeeUser2),REPLICATE('*',LEN(DataCannotSeeUser2))) AS DataCannotSeeUser2 FROM TableExample WHERE UserType = @UserType
    END
  END
 ELSE
   BEGIN
    SELECT DataCannotSeeUser1,
	       DataCannotSeeUser2 FROM TableExample
   END 
END 

create view ViewUser1
AS
SELECT DataCannotSeeUser2, 
       STUFF(DataCannotSeeUser1,2,LEN(DataCannotSeeUser1),REPLICATE('*',LEN(DataCannotSeeUser1))) AS DataCannotSeeUser1 FROM TableExample

create view ViewUser2
AS
       SELECT DataCannotSeeUser1, 
	          STUFF(DataCannotSeeUser2,2,LEN(DataCannotSeeUser2),REPLICATE('*',LEN(DataCannotSeeUser2))) AS DataCannotSeeUser2 FROM TableExample

 

 

The second approach was using EXECUTE AS based on, for example, a variable session of the application and session_context feature, for example:

 

  • Connect to master database and create the login:

 

CREATE LOGIN login1 WITH PASSWORD = 'ComplexPassword99!'  
  • Create database user, table example and give the permissions to the table to this user.

 

CREATE USER user1 FOR LOGIN login1;  

CREATE TABLE Contact
  (ID int IDENTITY PRIMARY KEY,
   Nombre varchar(100) MASKED WITH (FUNCTION = 'partial(1,"XXXXXXX",0)') NULL,
   Apellido varchar(100) NOT NULL,
   NrTlf varchar(12) MASKED WITH (FUNCTION = 'default()') NULL,
   Email varchar(100) MASKED WITH (FUNCTION = 'email()') NULL);
 
INSERT Contact (Nombre, Apellido, NrTlf, Email) VALUES 
('Roberto', 'Torres', '91551234567', 'RTorres@contoso.com'),
('Juan', 'Galvin', '95551234568', 'JGalvin@contoso.com'),
('José', 'Garcia', '95551234569', 'Jgarcia@contoso.net');

GRANT SELECT ON Contact to User1;
  • Add the value of the parameter UserToFilter by the name of the user, after stablishing the connection.
EXEC sp_set_session_context 'UserToFilter', 'User1'
  • Execute AS example to filter data:
EXECUTE AS USER = convert(varchar(200),SESSION_CONTEXT(N'UserToFilter')) 
SELECT * FROM Contact;
REVERT; 

Enjoy!