Allow login to run stored procedure without being able to select from table

非 Y 不嫁゛ 提交于 2021-02-19 05:33:44

问题


I have a SQL Server with two databases:

  • Database1
  • Database2

Login 'MyLogin' has read access on Database2 only.

Database2 has a stored procedure as follows:

CREATE PROCEDURE Get_LogData 
       @ProductID int
AS
BEGIN

    If Exists (Select (1) From Database2.dbo.Products Where ProductID = @ProductID)
    Select LogTimeStamp
         , ProductID
         , Description
    from Database1.dbo.Log Where ProductID = @ProductID
    Else
    Print 'Get_LogData: Unable to find ProductID ' + CAST(@ProductID AS VARCHAR)

END;
GO

BEGIN
    GRANT EXEC ON Get_LogData TO [MyLogin];
END;
GO

When 'MyLogin' trys to run the stored procedure, we get the error:

The server principal "MyLogin" is not able to access the database "Database1" under the current security context.

How do I allow 'MyLogin' the ability to run this stored proc and get the data from Database1, without allowing them to just run a normal Select query against Database1.Log


回答1:


This is expanding on the answer linked by Ezlo (SQL Server EXECUTE AS trouble), on which I'm going to cover Enabling Cross-Database Access in SQL Server.

Firstly, let's set up a quick test to replicate the issue you have right now:

--Create a couple of sample databases
CREATE DATABASE SampleDB1;
CREATE DATABASE SampleDB2;
GO

USE SampleDB2;
GO
--Create a sample table
CREATE TABLE dbo.SampleTable (ID int, Somestring varchar(25));
GO
USE SampleDB1;
GO
--Create a sample SP and User/Login;
CREATE PROC dbo.SomeProc AS

    SELECT ID,
           SomeString
    FROM SampleDB2.dbo.SampleTable;
GO
CREATE LOGIN SampleCredential WITH PASSWORD = 'abc123', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF, DEFAULT_LANGUAGE = BRITISH;
CREATE USER SampleCredential FOR LOGIN SampleCredential;

GRANT EXEC ON dbo.SomeProc TO SampleCredential;
GO

--Test
EXECUTE AS LOGIN = 'SampleCredential';
GO
--This will fail
EXEC dbo.SomeProc;
GO
REVERT;
GO

As you can see, if you run this script, you get an error along the lines of:

Msg 916, Level 14, State 1, Procedure SomeProc, Line 4 [Batch Start Line 28] The server principal "SampleCredential" is not able to access the database "SampleDB2" under the current security context.

So, what is Cross-Database access? To quote from the documentation:

Cross-database ownership chaining occurs when a procedure in one database depends on objects in another database. A cross-database ownership chain works in the same way as ownership chaining within a single database, except that an unbroken ownership chain requires that all the object owners are mapped to the same login account. If the source object in the source database and the target objects in the target databases are owned by the same login account, SQL Server does not check permissions on the target objects.

Note, however, that there are major security flaws that can be introduced with this method. Thus, if this is a concern for your environment this is not the solution for you. Again, from the documentation:

Ownership chaining across databases is turned off by default. Microsoft recommends that you disable cross-database ownership chaining because it exposes you to the following security risks:

  • Database owners and members of the db_ddladmin or the db_owners database roles can create objects that are owned by other users. These objects can potentially target objects in other databases. This means that if you enable cross-database ownership chaining, you must fully trust these users with data in all databases.

  • Users with CREATE DATABASE permission can create new databases and attach existing databases. If cross-database ownership chaining is enabled, these users can access objects in other databases that they might not have privileges in from the newly created or attached databases that they create.

OK, now that caveat is out of the way, what do to. Continuing on from the script above, we need to enable cross db ownership chaining on the server if it isn't already. You can do so by running the following:

EXEC sp_configure 'show advanced', 1;
RECONFIGURE;  
GO
EXEC sp_configure 'cross db ownership chaining', 1;  
RECONFIGURE;  
GO
EXEC sp_configure 'show advanced', 0;
RECONFIGURE;  
GO

Now that is enabled you can enable DB_CHAINING on the 2 databases:

ALTER DATABASE SampleDB1 SET DB_CHAINING ON;
ALTER DATABASE SampleDB2 SET DB_CHAINING ON;
GO

You'll then need to create the user for your login on the other database, but won't need to give it any permissions, so this will be "fine" (it won't allow them to SELECT from, the table outside of the SP):

USE SampleDB2;
GO
CREATE USER SampleCredential FOR LOGIN SampleCredential;
GO

Finally, you can then test again:

USE SampleDB1;
--Test
EXECUTE AS LOGIN = 'SampleCredential';
GO
--It works (0 rows returned)
EXEC dbo.SomeProc;
GO
REVERT;
GO

If you want to double check the user can't select from the database, you can do so:

USE SampelDB1;
GO
--This will fail
SELECT *
FROM SampleDB2.dbo.SampleTable;
GO

And finally the cleanup (for good measure):

--Cleanup
USE master;
DROP DATABASE SampleDB2;
DROP DATABASE SampleDB1;
DROP LOGIN SampleCredential;
EXEC sp_configure 'show advanced', 1;
RECONFIGURE;  
GO
EXEC sp_configure 'cross db ownership chaining', 0;  
RECONFIGURE;  
GO
EXEC sp_configure 'show advanced', 0;
RECONFIGURE;  
GO



回答2:


You want to use the execute as clause. It is unclear what the value should be, but to set permissions, often you want the owner of the stored procedure to have the permissions you want.

CREATE PROCEDURE Get_LogData (
       @ProductID int
)
WITH EXECUTE AS OWNER AS
BEGIN
    . . .
END;


来源:https://stackoverflow.com/questions/54235017/allow-login-to-run-stored-procedure-without-being-able-to-select-from-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!