I'm looking for a solution where a user's only permission it to execute a procedure. In my example below, that procedure is lowlevel.ZipFile
(Main Procedure). The problem is that the procedure itself executes other procedures which causes errors.. As long as the user executes the main procedure and not any of the nested procedures themselves I want it to be approved. I've looked around and found that the easiest way to deny access to execute other stored is to create a new schema. I then Created an other user that has access to the nested procedures, and created my main procedureWITH EXECUTE AS
signed to the user who has access. However,when I execute the main procedure with that user, he all of a sudden doesn't have permission to execute the nested procedure?
The code below is for testing purposes. Be aware if you've a database named "SiteDatabase44", a user named "LowLevelUser_Executor" or a procedure named "___ZipFile" in the master database. A clean up-script is commented at the end.
SET NOCOUNT ON
USE master;
IF NOT EXISTS(SELECT 1 FROM sys.databases WHERE Name = 'SiteDatabase44')
EXECUTE('CREATE DATABASE SiteDatabase44')
IF NOT EXISTS (SELECT 1 FROM sys.syslogins WHERE NAME = 'LowLevelUser_Executor')
BEGIN
CREATE LOGIN [LowLevelUser_Executor] WITH PASSWORD = N'ShouldNeverBeLoggedInAs_asdasd', CHECK_POLICY = OFF;
GRANT CONNECT SQL TO [LowLevelUser_Executor]
END
GO
CREATE OR ALTER PROCEDURE dbo.___ZipFile
@Filepath varchar(500) NULL
AS
SELECT 22
GO
IF NOT EXISTS(SELECT 1 FROM sys.sysusers WHERE name = 'LowLevelUser_Executor')
CREATE USER LowLevelUser_Executor FOR LOGIN LowLevelUser_Executor WITH DEFAULT_SCHEMA = dbo
GRANT EXECUTE ON [dbo].___ZipFile TO [LowLevelUser_Executor]
USE [SiteDatabase44];
IF NOT EXISTS(SELECT 1 FROM sys.schemas WHERE [name] = 'lowlevel')
EXECUTE('CREATE SCHEMA lowlevel')
GO
IF NOT EXISTS(SELECT 1 FROM sys.sysusers WHERE name = 'LowLevelUser_Executor')
CREATE USER [LowLevelUser_Executor] FOR LOGIN [LowLevelUser_Executor] WITH DEFAULT_SCHEMA = dbo
GO
CREATE OR ALTER PROCEDURE lowlevel.ZipFile
@Filepath varchar(500) NULL
WITH EXECUTE AS 'LowLevelUser_Executor'
AS
SELECT 'Zipping file...'
EXECUTE master.dbo.___ZipFile @Filepath
GO
GRANT SELECT, EXECUTE ON SCHEMA::lowlevel TO [LowLevelUser_Executor] WITH GRANT OPTION;
EXECUTE AS LOGIN = 'LowLevelUser_Executor'
exec master.dbo.___ZipFile @Filepath = '' -- Works as expected
EXECUTE [lowlevel].ZipFile @Filepath = '' -- Doesn't work? Get's this error: The EXECUTE permission was denied on the object '___ZipFile', database 'master', schema 'dbo'.
go
REVERT;
/*
CLEAN UP
USE MASTER;
DROP DATABASE SiteDatabase44
GO
DROP PROCEDURE dbo.___ZipFile
GO
DROP USER [LowLevelUser_Executor]
GO
DROP LOGIN [LowLevelUser_Executor]
*/
1条答案
按热度按时间1hdlvixo1#
As I mention in the comments, the problem here is that you have a cross database reference. When working in the same database ownership chaining can occur, however, for cross databases this is off by default. Though it could be configured, there are (multiple) security implications with such a set up, so without knowing your environment I can't recommend it.
As a result an alternative would be to implement certificates. This is a simplified example, you may want/need to encrypt your certificates with a password, for example. if you do you'll need to pass the private key to the
CREATE CERTIFICATE
statement (which can obtained usingCERTPRIVATEKEY
) when creating it in the second database. This is the simplest set of steps to get to the end goal; you will likely need more specific to your needs.First, lets create a couple of test databases, and a test login:
Now I'm going to create a procedure in both databases, with one referencing the other. Then I'll create a
USER
for the testLOGIN
andGRANT
them permission toEXECUTE
the procedure that references the other:Great, now we have a similar set up to what you do. We can now test to see if things work (they won't):
Msg 916, Level 14, State 2, Procedure dbo.AnotherProc, Line 5 [Batch Start Line 32]
The server principal "AnotherLogin" is not able to access the database "SomeDatabase" under the current security context.
So yeah, that was expected.
So, let's create a certificate. As this is a new test database I need to create a
MASTER KEY
, but this may well already exist in your database (if it does, you don't need to create one again, but for the purposes here I do). I also then sign the procedure that we created before. As mentioned before, the certificate isn't encrypted, but this is something you may want/need to do.Great, but now we need to alsoc create that certificate in the other database. Fortunately this is much easier than the days of SQL Server 2008, and we can get the binary value of the cert using
CERTENCODED
. As, however,CREATE CERTIFICATE
needs a literal value, we'll grab the value and concatenate it to a "dynamic" batch and usesys.sp_executesql
to execute that batch. Like before, as this is a test, it create aMASTER KEY
but you may well not need to do that.Great, we're almost there. Finally we need to create a
USER
based on that cert in the other databaseSomeDatabase
and also create aUSER
for theLOGIN
AnotherLogin
in the database. Note, however, I don't give any permissions to theUSER
, but they do still need to be able to connect to the other database. Finally I give the certificate'sUSER
permission toEXECUTE
the procedure, (notice it is notAnotherUser
).Ok, we are now there and we can test again:
Success! The procedure executes the procedure that calls
SomeDatabase.dbo.SomeProc
, but the call directly to that procedure fails.Clean-up:
Of course, the alternative would be to just give the
LOGIN
'sUSER
inmaster
permission to executedbo.___ZipFile
, but it appears you explicitly don't want to do that.