How does impersonation and security permissions in

2019-08-18 07:14发布

问题:

I have a tSQLt test which I expect to fail but it runs with success. When I run the code outside the procedure, it fails as expected, but when executing the test with Run, no error occurs.

I have read the question tSQLt Testing SQL Server security permissions but the accepted answer does not solve my problem.

My test looks like this:

    CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
    AS 
    BEGIN
        --EXEC tSQLt.ExpectException
        EXECUTE AS USER = 'AFDK_Reader'

        select *
        from sys.user_token

        SELECT * FROM fn_my_permissions('AWS', 'SCHEMA')   
        ORDER BY subentity_name, permission_name ;   

        SELECT *
        FROM [AWS].[ADRESSEPUNKT_HISTORIK]
        REVERT
    END

The role has granted select permissions on the AFDK schema only and that is the only database role membership the SQL user has.

The AFDK_Reader has no permissions to read from the AWS schema.

Can anybody tell me how to get on with my debugging? Thanks in advance.

回答1:

EXECUTE AS... REVERT commands don't behave in the way you are expecting inside a stored procedure. This is a general feature of stored procedure security in SQL Server; one common use of stored procedures is to abstract permissions. The MS docs page Customizing Permissions with Impersonation in SQL Server says:

SQL Server does not check the permissions of the caller if the stored procedure and tables have the same owner.

and that's effectively what's happening here. Even though the EXECUTE AS changes the security context, that security context isn't checked inside the stored procedure.

The docs page also says:

However, ownership chaining doesn't work if objects have different owners or in the case of dynamic SQL.

One way to get the behaviour you're expecting would be to run the SELECT statement from inside a dynamic SQL statement, which means that the active security context is tested against the table permissions:

CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS 
BEGIN
    EXEC tSQLt.ExpectException
    EXECUTE AS USER = 'AFDK_Reader'

    EXEC ('SELECT * FROM [AWS].[ADRESSEPUNKT_HISTORIK]')
    REVERT
END

A alternative (better?) solution might be to use the built-in permission functions to test permissions settings through metadata. Here's one way, still using EXECUTE AS... REVERT and sys.fn_my_permission:

CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS 
BEGIN
    EXECUTE AS USER = 'AFDK_Reader'
    DECLARE @permissionCount int = (SELECT COUNT(*) FROM sys.fn_my_permissions('[AWS].[ADRESSEPUNKT_HISTORIK]', 'OBJECT') WHERE permission_name = 'SELECT' AND subentity_name = '')
    REVERT
    EXEC tSQLt.AssertEquals 0, @permissionCount
END