One thing that I've always hated more than just about anything in MS SQL Server is the way that security works. The security context constantly switches if you look at the server funny and it's often very hard (for me anyway) to predict or debug.
In dealing with an issue today, I though, "I wish I could just add a line to my code that would display the security context that SQL Server is using when this code runs." Does such a command exist? For example, SELECT security_context()
To be a little clearer... if I'm in a stored procedure and am therefor subject to the security context of the owner of the SP then I'd like to see that. If I'm in code that was called by sp_executesql and it's causing the security to be under the context of the SQL Server service account, then I would want to see that.
At least then I might be able to figure out why SQL Server thinks that I shouldn't have access to something.
Thanks!
EXAMPLE
-- Set up
CREATE USER Test_User WITHOUT LOGIN
CREATE TABLE Test_Security_Context (my_id INT)
INSERT INTO Test_Security_Context VALUES (1)
DENY SELECT ON Test_Security_Context TO Test_User
GO
CREATE PROCEDURE Test_Security_Context_SP
AS
SELECT SUSER_SNAME()
SELECT * FROM Test_Security_Context -- This will return ok
EXEC('SELECT SUSER_SNAME(); SELECT * FROM Test_Security_Context') -- SUSER_SNAME() will match above but select fails
GO
GRANT EXECUTE ON Test_Security_Context_SP TO Test_User
GO
-- Switch to the new user
SETUSER 'Test_User'
GO
-- Do the test
EXEC Test_Security_Context_SP
GO
-- Clean up
SETUSER
DROP PROCEDURE Test_Security_Context_SP
DROP TABLE Test_Security_Context
DROP USER Test_User
GO
Yes, there is such a pair of views that represents your current security context, considering all the details like EXECUTE AS or code signing:
sys.login_token for the server wide context
sys.user_token for the current database context
Every single access you get is ultimately derived from a row in the return of these results. Note that some access are implicit from hard coded role membership (like db_datareader database role or sysadmin server role).
Other that that:
ownership chaining is not related to security context: you are not under the 'context' of the SP owner. Ownership chaining simply states that access checks are skipped for objects owned by the same owner as current object (SP, View).
sp_executesql does not change the security context in any way
Not sure if this is what you mean by security context, but you can retrieve the user associated with your session like:
select SYSTEM_USER
This works for both a SQL Server login or a WIndows login. It even works inside stored procedures with execute as owner. For example,
create procedure dbo.Test
with execute as owner
as
select SYSTEM_USER
go
exec dbo.Test
select SYSTEM_USER
Prints:
sa
MyMachine\MyName
If you're looking for the Windows account that SQL Server is using to do things on your behalf, you could try to run whoami from the command like:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
EXEC master..xp_cmdshell 'whoami'
For me, that returns nt authority\network service.
I think you want to use CURRENT_USER to see the current security context. Here's an example:
SELECT CURRENT_USER AS 'Current User Name';
GO
EXECUTE AS LOGIN = 'junk'
GO
SELECT CURRENT_USER AS 'Current User Name';
GO
REVERT
SELECT CURRENT_USER AS 'Current User Name';
GO
with output (note: I'm admin on my SQL Server for this)
Current User Name
------------------
dbo
(1 row(s) affected)
Current User Name
------------------
Junk
(1 row(s) affected)
Current User Name
------------------
dbo
(1 row(s) affected)
Related
I have the following issue:
I have two different databases, db1 and db2. I have an application that loads data into db2 or db3. db1 has a few tables that the application uses to determine behavior, including which db the application should load data into.
Users need to have write access to db1 to operate the application (there is a console application that writes to tables in db1, operating with windows authentication).
Users should not have DML privileges to db2 and db3, with the exception of a few predetermined operations. We grant AD groups database roles to control access from and organization perspective. Specifically, I'm trying to build a stored procedure in db1 that operators can use to reverse data loaded to db2 or db3 with appropriate logging.
I'm attempting to use create proc ... execute as owner to accomplish this, but it does not seem to be working when I try to hit tables in db2/db3 (I'm thinking that "execute as owner" operates on db level users an not server level logins?). The following causes a permission error stating that the owner (myself) does not have permissions to db2/db3.
use db1
go
create proc dbo.wrapper #recordid int
as begin
/*
capturing user
*/
declare #usr varchar(255) = SUSER_SNAME()
exec dbo.inner #usr , #recordid
end
use db1
go
create proc dbo.inner #usr varchar(255), #recordid int
with execute as owner
as begin
/*
logic to determine whether to update db2 or db3 goes here
*/
insert db2.rolled_back
select * , #usr from db2.transactions where id = #recordid
delete from db2.transactions where id = #recordid
insert db3.rolled_back
select * , #usr from db3.transactions where id = #recordid
delete from db3.transactions where id = #recordid
end
Is there a way to get this to work? I've heard that certificate signing could do this, does anyone have any experience using certificate users. Our DBA's would rather not have to maintain certificates, so if there is a way to get this to work without certificates that would be best.
Any advice would be helpful.
Thank You!
I'm going to cover the cross database chaining side of thing here. note that there are certainly security considerations when using this method. For example someone with permissions to create objects in one database can give themselves access to data in another database with the owner, when they themselves have no access to the other database. The security concerns, however, are out of scope of this answer.
Firstly, let's create a couple of test databases.
USE master;
GO
CREATE DATABASE Chain1;
CREATE DATABASE Chain2;
Now I'm going to CREATE a LOGIN, which is disable and make that the owner of these 2 databases. The databases having the same owner is important, as otherwise the chaining won't work.
CREATE LOGIN ChainerOwner WITH PASSWORD = N'SomeSecurePassword123';
ALTER LOGIN ChainerOwner DISABLE;
GO
ALTER AUTHORIZATION ON DATABASE::Chain1 TO ChainerOwner;
ALTER AUTHORIZATION ON DATABASE::Chain2 TO ChainerOwner;
I'm also going to create a LOGIN which we're going to use to test on:
CREATE LOGIN SomeUser WITH PASSWORD = N'SomeSecurePassword123';
Great, now I can create a few objects; a table in Chain1, a PROCEDURE in Chain2 that accesses the TABLE, and a USER in both databases for SomeUser. In Chain1 the USER will be given no permissions, and in Chain2 the user will be given the permsision to EXECUTE the PROCEDURE:
USE Chain1;
GO
CREATE TABLE dbo.SomeTable (I int IDENTITY,
S varchar(10));
INSERT INTO dbo.SomeTable (S)
VALUES ('abc'),
('xyz');
GO
CREATE USER SomeUser FOR LOGIN SomeUser;
GO
USE Chain2;
GO
CREATE PROC dbo.CrossDBProc #I int AS
BEGIN
SELECT I,
S
FROM Chain1.dbo.SomeTable
WHERE I = #I;
END;
GO
CREATE USER SomeUser FOR LOGIN SomeUser;
GO
GRANT EXECUTE ON dbo.CrossDBProc TO SomeUser;
GO
Great, all the objects are created, now let's try to EXECUTE that PROCEDURE:
EXECUTE AS LOGIN = 'SomeUser';
GO
EXEC dbo.CrossDBProc 1; --This fails
GO
REVERT;
GO
This fails, with a permission error:
The SELECT permission was denied on the object 'SomeTable', database 'Chain1', schema 'dbo'.
This is expected, as there is no ownership chaining. let's, therefore enable that now.
USE master;
GO
ALTER DATABASE Chain1 SET DB_CHAINING ON;
ALTER DATABASE Chain2 SET DB_CHAINING ON;
Now if I try the same again, the same SQL works:
USE Chain2;
GO
EXECUTE AS LOGIN = 'SomeUser';
GO
EXEC dbo.CrossDBProc 1; --This now works
GO
REVERT;
GO
This successfully returns the result set
I
S
1
abc
So, yes you can chain cross database, but it requires some set up, and (again) there are security considerations you need think about.
Clean up:
USE master;
GO
DROP DATABASE Chain1;
DROP DATABASE Chain2;
GO
DROP LOGIN ChainerOwner;
DROP LOGIN SomeUser;
My client just upgraded from SQL Server 2000 to SQL Server 2016. One stored procedure used 15-20 times per day runs through a table of file names and reports for each on whether it exists.
Previously it used xp_FileExist, but it now seems to require admin rights to be used. I can't give all 176 users admin rights and keep my job.
I found articles suggesting use of GRANT IMPERSONATE and EXECUTE AS USER but can't get it to work.
-- Old code works only for administrators
-- non-administrators ALWAYS get 0 for result even when file exists
EXEC master.dbo.xp_fileexist #docUNC, #FileExists OUTPUT
-- code run using admin account
SELECT SUSER_NAME() LOGIN_Name, USER_NAME() username;
Results
LOGIN_Name username
--------------------------------------
DomainName\potomcandice dbo
-- attempted new code for grant impersonate (want to let maronj impersonate potomcandice)
GRANT IMPERSONATE ON USER:: maronj TO potomcandice
-- New code
EXECUTE AS USER='potomcandice'
EXEC master.dbo.xp_fileexist #docUNC, #FileExists OUTPUT
Still get 0 for result even when the file does exist.
Hoping for suggestions on either
a) how to use "Grant Impersonate" and "Execute as User/Login" or
b) alternatives to xp_fileexist that do not involve xp_commandshell.
I use xp_fileexist inside a function that I found here and ran into the same problem where it didn't return correct results to non-admins. The solution in my environment was to simply add WITH EXECUTE AS OWNER to the function definition as shown below.
CREATE OR ALTER FUNCTION dbo.fnFileExists(#Path VARCHAR(8000)) RETURNS BIT WITH EXECUTE AS OWNER AS
/* Returns 1 if a file exists at the suppied Path. Returns 0 if it doesn't.
Uses WITH EXECUTE AS OWNER to enable use by non-admins.
Source: https://www.tech-recipes.com/rx/30527/sql-server-how-to-check-if-a-file-exists-in-a-directory/
*/
BEGIN
DECLARE #Result INT
EXEC master.dbo.xp_fileexist #Path, #Result OUTPUT
RETURN #Result
END
Find your SP in Object Explorer, go to SP Properties. In an opened window go to Permissions
Then click "Search" under the SP name.
hit "Browse" to find object name and mark "Public". Click OK.
Then Mark Grant "Execute" and try if it works.
This should do the trick
The only user that can see the entire sys.sysprocesses is the SA. Is there a role I can place a user (or any other way) that a user can be made a member of so the user can see the entire sys.sysprocesses -all the rows for all users not just the processes for the user executing the select.
I connect to many SQL Server instances with a dbo account. I need to know if someone is connected to the instance. I cannot get SA privileges.
You can use code signing to accomplish what you're looking to do. Here's the code:
CREATE LOGIN [normalUser] WITH password = 'f00bar!23'
CREATE USER [normalUser]
GO
CREATE CERTIFICATE [codeSigningCert] WITH SUBJECT = 'Certificate for code signing', EXPIRY_DATE = '2099-01-01'
CREATE LOGIN [codeSigningLogin] FROM CERTIFICATE [codeSigningCert]
GRANT VIEW SERVER STATE TO [codeSigningLogin]
go
CREATE PROCEDURE dbo.processesProc
AS
BEGIN
SELECT *
FROM sys.sysprocesses AS s
END
GO
GRANT EXECUTE ON dbo.processesProc TO [normalUser]
GO
EXECUTE AS LOGIN = 'normalUser'
GO
EXEC dbo.processesProc
GO
REVERT
GO
ADD SIGNATURE TO processesProc BY CERTIFICATE [codeSigningCert]
GO
GRANT EXECUTE ON dbo.processesProc TO [normalUser]
GO
EXECUTE AS LOGIN = 'normalUser'
GO
EXEC dbo.processesProc
GO
REVERT
GO
By way of explanation, I'm creating an unprivileged login/user and a stored procedure for them to run. Without signing the procedure, the select exhibits the normal behavior (that is, it only displays the current process. But once I add a signature to the procedure using a certificate that has an associated login with the proper permissions, the result set blossoms.
Note: I created the procedure in master because I'm lazy. You can create the procedure anywhere you want so long as the certificate exists in the database you're creating the procedure in (so you can add the signature). Lastly, one gotacha with code signing is that the signature is lost when the procedure is modified. This makes sense as the signature is an attestation of the contents of the proc at the time of signing. If the body is changed, it will need to be re-signed.
I don't think this is possible, but I hope I am missing something. Let's say I have this stored procedure in SQL Server 2012 SP3:
CREATE PROCEDURE [dbo].[myproc]
AS
SELECT * FROM [dbo].[mytable]
I want to grant a database principal the permission to execute [myproc] without granting the permission to SELECT on the underlying table.
I have tried all sorts of GRANT statements with no luck. When I execute myproc, I still get the error
select permission denied on mytable
The database principal is not the DB Owner and I do not want to make him the DB Owner.
Is what I am trying to do possible?
Thanks.
Weicher,
Unless there's a specific deny on the underlying table this grant statement should work:
GRANT Exec [dbo].[myproc] TO MyUser;
GO
Test it with code like this:
EXECUTE AS USER = 'MyUser';
GO
EXEC [dbo].[myproc]; -- should work
GO
SELECT id FROM [dbo].[table]; -- should fail
GO
REVERT;
When I've changed code six ways to Sunday, I like to start clean to make sure I didn't bollix something in inadvertently and be very purposeful. step. by. step.
Make a test table, Grant MyUser read rights - can you run a select
using the execute as user code?
Remove read rights, does the select work?
If it didn't, the world is still sane (somewhat). Now try with a new stored procedure
Grant execute rights to MyUser - does it work?
If it does, then follow the same test on the actual table - Can you grant read rights, then can read rights be removed. If so recreate the SP and try again.
And sorry if that was too simplistic, no offense intended just wanted to make sure we were on the same page testing wise.
If it doesn't work, post the results of the test and I'll try to help further.
GRANT EXECUTE ON SPNAME TO DBUSER
If you don't want to make the DB Principle the owner then you need to grant it permissions with 'Grant With' in order to have them passed on further.
Assuming 'dbo' is the owner and 'ElevatedUser' is a new user with select permissions
GRANT EXECUTE ON [dbo].[myproc] TO ElevatedUser
WITH GRANT OPTION AS [dbo]
GRANT EXECUTE ON [dbo].[myproc] TO MyUser AS ElevatedUser
or if you are really stuck you can put it inside the stored procedure itself
CREATE PROCEDURE [dbo].[myproc]
WITH EXECUTE AS 'dbo'
AS
BEGIN
For a .net application we can store database connectionstrings like so
<connectionstrings config="cnn.config" />
I am trying to get as little as permissions as possible but there always seems to be a different way. To get info.
I am using the settings because they are more secure for one and two I have other people working on my application.
So I've set
USE database_name;
GRANT EXECUTE TO [security_account];
So the user can execute commands that's fine.
Then I've got db_reader and db_writer so they can read and write and this seems like a perfect marriage.
Still bad news the user can login and see the tables and store procedures definitions but not alter them, however, they can still see them.
How can I set the permissions to where the user can read, write. execute, and that is it PERIOD!?
Being able to see the definition of tables, stored procedures, etc is governed by the VIEW DEFINITION permission. So you can do:
DENY VIEW DEFINITION TO [yourUser];
And that user won't be able to see the definition for any object in the database. This also includes the ability to see other users.
If you want to stop the user from viewing the sp definition you need to specify the WITH ENCRYPTION option in the sp.
Adding a user to the db_datareader and db_datawriter role and granting EXECUTE will limit the user to reading writing and executing but they will still be able to see the sp definition by using the sp_helptext stored procedure and sys.sql_modules catalog view. See here for more information on sp and funciton encryption.
exec sp_addrolemember #rolename = 'db_datareader', #membername = 'testUser'
exec sp_addrolemember #rolename = 'db_datawriter', #membername = 'testUser'
GRANT EXECUTE TO testUser;
You can create an sp the WITH ENCRYPTION option by adding it before the AS keyword like this. See the WITH ENCRYPTION section of the CREATE PROCEDURE definition here
CREATE PROCEDURE HumanResources.uspEncryptThis
WITH ENCRYPTION
AS
SET NOCOUNT ON;
SELECT BusinessEntityID, JobTitle, NationalIDNumber, VacationHours, SickLeaveHours
FROM HumanResources.Employee;
GO
You can also alter functions by adding it after the returns keyword.
ALTER FUNCTION dbo.getHash ( #inputString VARCHAR(20) )
RETURNS VARBINARY(8000) WITH ENCRYPTION