행 수준 보안 구현

완료됨

RLS(행 수준 보안)는 암호화를 사용하지 않으며 그룹 멤버 자격 또는 권한 부여 컨텍스트에 따라 보안 정책을 사용하여 데이터베이스 수준의 작동을 통해 테이블에 대한 액세스를 제한합니다. 이는 기능적으로 WHERE 절과 동일합니다.

보안 정책은 인라인 테이블 반환 함수를 호출하여 테이블의 행에 대한 액세스를 보호합니다.

사용자의 특성에 따라 조건자는 해당 사용자가 관련 정보에 액세스할 수 있는지 여부를 결정합니다. 테이블에 대해 쿼리를 실행하면 보안 정책이 조건자 함수를 적용합니다. 비즈니스 요구 사항에 따라 RLS는 WHERE CustomerId = 29만큼 간단하거나 필요한 경우 복잡할 수 있습니다.

행 수준 보안에서 지원하는 보안 정책에는 다음 두 가지 유형이 있습니다.

  • 필터 조건자 - 조건자를 위반하는 데이터 액세스를 제한합니다.

    Access 정의
    SELECT 필터링된 행을 볼 수 없습니다.
    UPDATE 필터링된 행을 업데이트할 수 없습니다.
    DELETE 필터링된 행을 삭제할 수 없습니다.
    INSERT 해당 사항 없음
  • 조건자 차단 - 조건자를 위반하는 데이터 변경을 제한합니다.

    Access 정의
    삽입 후 사용자가 조건자를 위반하는 값이 있는 행을 삽입할 수 없습니다.
    AFTER UPDATE 사용자가 조건자를 위반하는 값으로 행을 업데이트할 수 없습니다.
    업데이트 전 사용자가 현재 조건자를 위반하는 행을 업데이트할 수 없습니다.
    삭제 전 행이 조건자를 위반하는 경우 삭제 작업을 차단합니다.

액세스 제어는 데이터베이스 수준에서 구성되고 적용되므로 애플리케이션 변경은 있어도 최소한에 그칩니다. 또한 사용자는 테이블에 직접 액세스할 수 있으며 자신의 데이터를 쿼리할 수도 있습니다.

행 수준 보안은 다음 세 가지 주요 단계로 구현됩니다.

  1. 액세스를 격리할 사용자 또는 그룹을 만듭니다.
  2. 정의된 조건자를 기반으로 결과를 필터링하는 인라인 테이블 반환 함수를 만듭니다.
  3. 위에서 만든 함수를 할당하여 테이블에 대한 보안 정책을 만듭니다.

아래 T-SQL 명령은 사용자 액세스 권한이 테넌트에 의해 분리되는 시나리오에서 RLS를 사용하는 방법을 보여 줍니다.

-- Create supporting objects for this example
CREATE TABLE [Sales] (SalesID INT, 
    ProductID INT, 
    TenantName NVARCHAR(10), 
    OrderQtd INT, 
    UnitPrice MONEY)
GO

INSERT INTO [Sales]  VALUES (1, 3, 'Tenant1', 5, 10.00);
INSERT INTO [Sales]  VALUES (2, 4, 'Tenant1', 2, 57.00);
INSERT INTO [Sales]  VALUES (3, 7, 'Tenant1', 4, 23.00);
INSERT INTO [Sales]  VALUES (4, 2, 'Tenant2', 2, 91.00);
INSERT INTO [Sales]  VALUES (5, 9, 'Tenant3', 5, 80.00);
INSERT INTO [Sales]  VALUES (6, 1, 'Tenant3', 5, 35.00);
INSERT INTO [Sales]  VALUES (7, 3, 'Tenant4', 8, 11.00);

-- View all the rows in the table  
SELECT * FROM Sales;

다음으로 사용자를 만들고 Sales 테이블에 대한 액세스 권한을 부여합니다. 이 예제에서 각 사용자는 특정 테넌트를 담당합니다. TenantAdmin 사용자에게는 모든 테넌트의 데이터를 볼 수 있는 액세스 권한이 있습니다.

CREATE USER [TenantAdmin] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant1] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant2] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant3] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant4] WITH PASSWORD = '<strong password>'
GO

GRANT SELECT ON [Sales] TO [TenantAdmin]
GO
GRANT SELECT ON [Sales] TO [Tenant1]
GO
GRANT SELECT ON [Sales] TO [Tenant2]
GO
GRANT SELECT ON [Sales] TO [Tenant3]
GO
GRANT SELECT ON [Sales] TO [Tenant4]
GO

다음으로 새 스키마, 인라인 테이블 반환 함수를 만들고 사용자에게 새 함수에 대한 액세스 권한을 부여합니다. WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' 조건자는 쿼리를 실행하는 사용자 이름이 TenantName 열 값과 일치하는지 평가합니다.

CREATE SCHEMA sec;  
GO  

--Create the filter predicate

CREATE FUNCTION sec.tvf_SecurityPredicatebyTenant(@TenantName AS NVARCHAR(10))  
    RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN	SELECT 1 AS result
			WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin';  
GO

--Grant users access to inline table-valued function

GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [TenantAdmin]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant1]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant2]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant3]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant4]
GO

--Create security policy and add the filter predicate
CREATE SECURITY POLICY sec.SalesPolicy  
ADD FILTER PREDICATE sec.tvf_SecurityPredicatebyTenant(TenantName) ON [dbo].[Sales]
WITH (STATE = ON);  
GO

이 시점에 액세스를 테스트할 준비가 완료됩니다.

EXECUTE AS USER = 'TenantAdmin';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant1';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant2';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant3';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant4';  
SELECT * FROM dbo.Sales;
REVERT;

TenantAdmin 사용자에게는 모든 행이 표시됩니다. Tenant1, Tenant2, Tenant3, Tenant4 사용자에게는 자신의 행만 표시됩니다.

보안 정책을 WITH (STATE = OFF);로 변경하면 사용자에게 모든 행이 표시됩니다.

Screenshot of T-SQL commands to alter a security policy.

참고 항목

공격자가 특별하게 작성된 WHERE 절로 쿼리를 작성하고 예컨대 0으로 나누기 오류 같이 WHERE 조건이 true인 경우 예외를 강제로 적용하면 정보가 유출될 위험이 존재합니다. 이를 사이드 채널 공격이라고 합니다. 행 수준 보안을 사용할 때 사용자가 임시 쿼리를 실행하는 기능을 제한하는 것이 현명합니다.

사용 사례

행 수준 보안은 다음을 비롯해 다양한 시나리오에 적합합니다.

  • 행 수준에서 부서별 액세스를 격리해야 하는 경우.
  • 고객의 데이터 액세스를 회사와 관련된 데이터로만 제한해야 하는 경우.
  • 규정 준수를 위해 액세스를 제한해야 하는 경우.

모범 사례

RLS를 구현할 때 고려해야 할 몇 가지 모범 사례는 다음과 같습니다.

  • RLS 개체, 조건자 함수 및 보안 정책에 대한 별도의 스키마를 만드는 것이 좋습니다.
  • 가능하면 항상 조건자 함수의 형식 변환을 피합니다.
  • 성능을 최대화하기 위해 조건자 함수에서 과도한 테이블 조인과 재귀를 사용하지 마세요.