행 수준 보안Row-Level Security

이 항목은 다음에 적용됩니다. 예SQL Server(2016부터)예Azure SQL Database아니요Azure SQL Data Warehouse아니요병렬 데이터 웨어하우스 THIS TOPIC APPLIES TO: yesSQL Server (starting with 2016)yesAzure SQL DatabasenoAzure SQL Data Warehouse noParallel Data Warehouse

행 수준 보안 그래픽Row level security graphic

행 수준 보안(RLS)을 통해 고객은 쿼리(예: 그룹 멤버십 또는 실행 컨텍스트)를 실행하는 사용자의 특징을 기반으로 데이터베이스 테이블의 행에 대한 액세스를 제어할 수 있습니다.Row-Level Security enables customers to control access to rows in a database table based on the characteristics of the user executing a query (e.g., group membership or execution context).

행 수준 보안(RLS)은 응용 프로그램의 보안 설계 및 코딩을 간소화합니다.Row-Level Security (RLS) simplifies the design and coding of security in your application. RLS를 사용하면 데이터 행 액세스에 제한을 둘 수 있습니다.RLS enables you to implement restrictions on data row access. 예를 들어, 작업자가 자신의 부서와 관련된 데이터 행에만 액세스할 수 있도록 하거나, 고객의 데이터 액세스를 회사와 관련된 데이터에만 액세스하도록 제한할 수 있습니다.For example ensuring that workers can access only those data rows that are pertinent to their department, or restricting a customer's data access to only the data relevant to their company.

액세스 제한 논리는 다른 응용 프로그램 계층의 데이터와 다소 떨어진 데이터베이스 계층에 위치합니다.The access restriction logic is located in the database tier rather than away from the data in another application tier. 데이터베이스 시스템은 모든 계층에서 데이터 액세스를 시도할 때마다 액세스를 제한합니다.The database system applies the access restrictions every time that data access is attempted from any tier. 이로 인해 보안 시스템의 노출 영역이 감소되어 보안 시스템이 더 안정적이고 강력해집니다.This makes your security system more reliable and robust by reducing the surface area of your security system.

CREATE SECURITY POLICY Transact-SQLTransact-SQL 문을 사용하고 인라인 테이블 반환 함수로서 만들어진 조건자에 의해 RLS를 구현합니다.Implement RLS by using the CREATE SECURITY POLICY Transact-SQLTransact-SQL statement, and predicates created as inline table valued functions.

적용 대상: SQL ServerSQL Server ( SQL Server 2016SQL Server 2016 ~ 현재 버전), SQL 데이터베이스SQL Database (이해하기)Applies to: SQL ServerSQL Server ( SQL Server 2016SQL Server 2016 through current version), SQL 데이터베이스SQL Database (Get it).

설명 Description

RLS는 두 가지 유형의 보안 조건자를 지원합니다.RLS supports two types of security predicates.

  • 필터 조건자는 읽기 작업(SELECT, UPDATE 및 DELETE)에 사용 가능한 행을 자동으로 필터링합니다.Filter predicates silently filter the rows available to read operations (SELECT, UPDATE, and DELETE).

  • 차단 조건자는 조건자를 위반하는 쓰기 작업(AFTER INSERT, AFTER UPDATE, BEFORE UPDATE, BEFORE DELETE)을 명시적으로 차단합니다.Block predicates explicitly block write operations (AFTER INSERT, AFTER UPDATE, BEFORE UPDATE, BEFORE DELETE) that violate the predicate.

    테이블의 행 수준 데이터에 대한 액세스는 인라인 테이블 반환 함수로 정의된 보안 조건자에 의해 제한됩니다.Access to row-level data in a table is restricted by a security predicate defined as an inline table-valued function. 그런 다음 함수가 호출되고 보안 정책에 의해 적용됩니다.The function is then invoked and enforced by a security policy. 필터 조건자의 경우 결과 집합으로부터 행이 필터링된 응용 프로그램에 대한 표시가 없습니다. 모든 행이 필터링되었으면 null 집합이 반환됩니다.For filter predicates, there is no indication to the application that rows have been filtered from the result set; if all rows are filtered, then a null set will be returned. 차단 조건자의 경우 조건자를 위반하는 모든 작업은 오류와 함께 실패합니다.For block predicates, any operations that violate the predicate will fail with an error.

    기본 테이블로부터 데이터를 읽는 동안 필터 조건자가 적용되며, 모든 get 연산, SELECT, DELETE(즉, 사용자가 필터링되는 행을 삭제할 수 없음) 및 UPDATE(즉, 행이 이후 필터링되면서 업데이트될 수는 있어도 사용자가 필터링된 행을 업데이트할 수는 없음)에 적용됩니다.Filter predicates are applied while reading data from the base table, and it affects all get operations: SELECT, DELETE (i.e. user cannot delete rows that are filtered), and UPDATE (i.e. user cannot update rows that are filtered, although it is possible to update rows in such way that they will be subsequently filtered). 차단 조건자는 모든 쓰기 작업에 영향을 줍니다.Block predicates affect all write operations.

  • AFTER INSERT 및 AFTER UPDATE 조건자는 사용자가 조건자를 위반하는 값으로 행을 업데이트하는 것을 방지할 수 있습니다.AFTER INSERT and AFTER UPDATE predicates can prevent users from updating rows to values that violate the predicate.

  • BEFORE UPDATE 조건자는 사용자가 현재 조건자를 위반하는 행을 업데이트하는 것을 방지할 수 있습니다.BEFORE UPDATE predicates can prevent users from updating rows that currently violate the predicate.

  • BEFORE DELETE 조건자는 삭제 작업을 차단할 수 있습니다.BEFORE DELETE predicates can block delete operations.

    필터 조건자와 차단 조건자 및 보안 정책 모두 다음 동작을 수행합니다.Both filter and block predicates and security policies have the following behavior:

  • 다른 테이블과 조인하거나 함수를 호출하는 조건자 함수를 정의할 수 있습니다.You may define a predicate function that joins with another table and/or invokes a function. SCHEMABINDING = ON을 사용하여 보안 정책을 만든 경우에는 조인 또는 함수를 쿼리에서 액세스할 수 있으며, 다른 추가 권한 검사 없이 올바르게 작동 합니다.If the security policy is created with SCHEMABINDING = ON, then the join or function is accessible from the query and works as expected without any additional permission checks. SCHEMABINDING = OFF를 사용하여 보안 정책을 만든 경우에는 대상 테이블을 쿼리하기 위해 이러한 추가 테이블 및 함수에 대해 SELECT 또는 EXECUTE 권한이 필요합니다.If the security policy is created with SCHEMABINDING = OFF, then users will need SELECT or EXECUTE permissions on these additional tables and functions in order to query the target table.

  • 정의되었지만 사용할 수 없는 보안 조건자가 있는 테이블에 대한 쿼리를 실행할 수 있습니다.You may issue a query against a table that has a security predicate defined but disabled. 필터링되거나 차단된 행은 영향을 받지 않습니다.Any rows that would have been filtered or blocked are not affected.

  • dbo 사용자, db_owner 역할의 멤버 또는 테이블 소유자가 보안 정책이 정의되고 사용되는 테이블에 대해 쿼리한 경우 행은 보안 정책에 의해 정의된 대로 필터링되거나 차단됩니다.If the dbo user, a member of the db_owner role, or the table owner queries against a table that has a security policy defined and enabled, the rows are filtered or blocked as defined by the security policy.

  • 스키마 바운드 보안 정책에 의해 바인딩된 테이블의 스키마를 변경하려 하면 오류가 발생합니다.Attempts to alter the schema of a table bound by a schema bound security policy will result in an error. 그러나 조건자에 의해 참조되지 않는 열은 변경할 수 있습니다.However, columns not referenced by the predicate can be altered.

  • 지정된 작업에 대해 정의된 조건자가 이미 있는 테이블에 조건자를 추가하려고 하면(사용 가능 여부에 관계없이) 오류가 발생합니다.Attempts to add a predicate on a table that already has one defined for the specified operation (regardless of whether it is enabled or disabled) results in an error.

  • 스키마 바운드 보안 정책의 경우 보안 정책 내의 테이블에 있는 조건자로 사용되는 함수를 변경하려고 하면 오류가 발생합니다.For schema bound security policies, attempts to modify a function used as a predicate on a table within a security policy results in an error.

  • 겹치지 않는 조건자를 포함하는 복수의 활성 보안 정책 정의가 이어집니다.Defining multiple active security policies that contain non-overlapping predicates, succeeds.

    필터 조건자는 다음 동작을 수행합니다.Filter predicates have the following behavior:

  • 테이블의 행을 필터링하는 보안 정책을 정의합니다.Define a security policy that filters the rows of a table. 응용 프로그램은 모든 행이 필터링된 경우를 포함하여 SELECT, UPDATEDELETE 연산에 대해 필터링된 모든 행을 인식하지 못합니다. 응용 프로그램은 다른 연산 도중 필터링될지 여부에 관계없이 모든 행을 INSERT 할 수 있습니다.The application is unaware that any rows have been filtered for SELECT, UPDATE, and DELETE operations, including situations where all the rows have been filtered out. The application can INSERT any rows, regardless of whether or not they will be filtered during any other operation.

    차단 조건자는 다음 동작을 수행합니다.Block predicates have the following behavior:

  • UPDATE에 대한 차단 조건자는 BEFORE 및 AFTER에 대해 별도의 작업으로 분할됩니다.Block predicates for UPDATE are split into separate operations for BEFORE and AFTER. 따라서 예를 들어 사용자가 행을 현재보다 큰 값으로 업데이트하는 것을 차단할 수 없습니다.Consequently, you cannot, for example, block users from updating a row to have a value higher than the current one. 이러한 종류의 논리가 필요한 경우 DELETED 및 INSERTED 중간 테이블과 함께 트리거를 사용하여 이전 값과 새 값을 함께 참조해야 합니다.If this kind of logic is required, you must use triggers with the DELETED and INSERTED intermediate tables to reference the old and new values together.

  • 최적화 프로그램은 조건자 함수에서 사용하는 열이 변경되지 않은 경우 AFTER UPDATE 차단 조건자를 확인하지 않습니다.The optimizer will not check an AFTER UPDATE block predicate if none of the columns used by the predicate function were changed. 예를 들어 Alice는 급여를 100,000보다 크게 변경할 수 없지만 급여가 이미 100,000을 초과하는(따라서 이미 조건자를 위반하는) 직원의 주소를 변경할 수는 있습니다.For example: Alice should not be able to change a salary to be greater than 100,000, but she should be able to change the address of an employee whose salary is already greater than 100,000 (and thus already violates the predicate).

  • BULK INSERT를 포함하여 대량 API에 적용된 변경 내용은 없습니다.No changes have been made to the bulk APIs, including BULK INSERT. 따라서 일반적인 삽입 작업과 마찬가지로 차단 조건자 AFTER INSERT가 대량 삽입 작업에 적용됩니다.This means that block predicates AFTER INSERT will apply to bulk insert operations just as they would regular insert operations.

사용 사례 Use Cases

RLS 사용 방법에 대한 설계 예는 다음과 같습니다.Here are design examples of how RLS can be used:

  • 병원에서 간호사가 담당 환자의 데이터 행만을 볼 수 있게 하는 보안 정책을 만들 수 있습니다.A hospital can create a security policy that allows nurses to view data rows for their own patients only.

  • 은행에서 직원의 업무 부서에 따라, 또는 회사 내에서 직원의 역할에 따라 금융 데이터 행에 대한 액세스를 제한하는 정책을 만들 수 있습니다.A bank can create a policy to restrict access to rows of financial data based on the employee's business division, or based on the employee's role within the company.

  • 다중 테넌트 응용 프로그램은 모든 다른 테넌트 행으로부터 각 테넌트의 데이터 행의 논리적 분리를 적용하는 정책을 만들 수 있습니다.A multi-tenant application can create a policy to enforce a logical separation of each tenant's data rows from every other tenant's rows. 여러 테넌트의 데이터를 하나의 테이블에 저장하여 효율성을 높일 수 있습니다.Efficiencies are achieved by the storage of data for many tenants in a single table. 물론 각 테넌트는 각자의 데이터 행만 볼 수 있습니다.Of course, each tenant can see only its data rows.

    RLS 필터 조건자는 WHERE 절을 추가한 것과 기능적으로 동일합니다.RLS filter predicates are functionally equivalent to appending a WHERE clause. 조건자는 업무 관례 명령처럼 복잡해질 수 있으며, 또는 절은 WHERE TenantId = 42처럼 간단해질 수 있습니다.The predicate can be as sophisticated as business practices dictate, or the clause can be as simple as WHERE TenantId = 42.

    더 공식적인 용어로는, RLS는 조건자 기반 액세스 제어를 말합니다.In more formal terms, RLS introduces predicate based access control. 여기에는 유연하고, 중앙 집중적이며, 조건자 기반 평가가 있어 메타데이터 또는 관리자가 적절히 판단한 기타 기준을 고려할 수 있습니다.It features a flexible, centralized, predicate-based evaluation that can take into consideration metadata or any other criteria the administrator determines as appropriate. 조건자는 사용자 특성에 따라 사용자가 데이터에 적절하게 액세스하는지의 여부를 결정하는 기준으로 사용됩니다.The predicate is used as a criterion to determine whether or not the user has the appropriate access to the data based on user attributes. 조건자 기반 액세스 제어를 사용하여 레이블 기반 액세스 제어를 구현할 수 있습니다.Label-based access control can be implemented by using predicate-based access control.

사용 권한 Permissions

보안 정책을 만들거나, 변경하거나, 삭제하는 데에는 ALTER ANY SECURITY POLICY 권한이 필요합니다.Creating, altering, or dropping security policies requires the ALTER ANY SECURITY POLICY permission. 보안 정책을 만들거나 삭제하려면 스키마에 대한 ALTER 권한이 필요합니다.Creating or dropping a security policy requires ALTER permission on the schema.

또한 추가된 각 조건자에는 다음 권한이 필요합니다.Additionally the following permissions are required for each predicate that is added:

  • 조건자로 사용되는 함수에 대한SELECTREFERENCES 권한.SELECT and REFERENCES permissions on the function being used as a predicate.

  • 정책에 바인딩되는 대상 테이블에 대한REFERENCES 권한.REFERENCES permission on the target table being bound to the policy.

  • 인수로 사용하는 대상 테이블의 모든 열에 대한REFERENCES 권한.REFERENCES permission on every column from the target table used as arguments.

    보안 정책은 데이터베이스의 dbo 사용자를 포함한 모든 사용자에게 적용됩니다.Security policies apply to all users, including dbo users in the database. Dbo 사용자는 보안 정책을 변경하거나 삭제할 수 있지만 해당 변경은 감사를 받을 수 있습니다.Dbo users can alter or drop security policies however their changes to security policies can be audited. 높은 권한이 있는 사용자(예: sysadmin 또는 db_owner)가 문제를 해결하거나 데이터의 유효성을 검사하기 위해 모든 행을 볼 수 있어야 하는 경우 이를 허용하도록 보안 정책을 작성해야 합니다.If high privileged users (such as sysadmin or db_owner) need to see all rows to troubleshoot or validate data, the security policy must be written to allow that.

    SCHEMABINDING = OFF를 사용하여 보안 정책을 만든 경우 대상 테이블을 쿼리하려면 조건자 함수에 대한 SELECT 또는 EXECUTE 권한과 조건자 함수 내에서 사용되는 추가 테이블, 뷰 또는 함수가 있어야 합니다.If a security policy is created with SCHEMABINDING = OFF, then to query the target table, users must have the SELECT or EXECUTE permission on the predicate function and any additional tables, views, or functions used within the predicate function. SCHEMABINDING = ON (기본값)을 사용하여 보안 정책을 만든 경우 사용자가 대상 테이블을 쿼리할 때 이러한 권한 검사는 무시됩니다.If a security policy is created with SCHEMABINDING = ON (the default), then these permission checks are bypassed when users query the target table.

최선의 구현 방법 Best Practices

  • RLS 개체(조건자 함수 및 보안 정책)에 대한 별도의 스키마를 만들 것을 적극 권장합니다.It is highly recommended to create a separate schema for the RLS objects (predicate function and security policy).

  • ALTER ANY SECURITY POLICY 권한은 보안 정책 관리자와 같은 높은 권한을 가진 사용자를 위한 것입니다.The ALTER ANY SECURITY POLICY permission is intended for highly-privileged users (such as a security policy manager). 보안 정책 관리자는 보호하는 테이블에 대한 SELECT 권한이 필요하지 않습니다.The security policy manager does not require SELECT permission on the tables they protect.

  • 잠재적인 런타임 오류를 방지하려면 조건자 함수에서 형식을 변환하지 마십시오.Avoid type conversions in predicate functions to avoid potential runtime errors.

  • 성능 저하를 방지하려면 조건자 함수에서 가능한 재귀를 피하십시오.Avoid recursion in predicate functions wherever possible to avoid performance degradation. 쿼리 최적화 프로그램은 직접 재귀를 감지하지만 간접 재귀(즉, 두 번째 함수가 조건자 함수를 호출하는 경우)를 찾아낸다고 보장할 수는 없습니다.The query optimizer will try to detect direct recursions, but is not guaranteed to find indirect recursions (i.e., where a second function calls the predicate function).

  • 성능을 최대화하기 위해 조건자 함수에서 과도한 테이블 조인을 사용하지 마십시오.Avoid using excessive table joins in predicate functions to maximize performance.

    세션별 SET 옵션에 종속되는 조건자 논리 방지: 실제 응용 프로그램에서는 사용되지 않지만 해당 논리가 특정 세션별 SET 옵션에 종속되는 조건자 함수는 사용자가 임의 쿼리를 실행할 수 있는 경우 정보를 누출할 수 있습니다.Avoid predicate logic that depends on session-specific SET options: While unlikely to be used in practical applications, predicate functions whose logic depends on certain session-specific SET options can leak information if users are able to execute arbitrary queries. 예를 들어 문자열을 암시적으로 datetime 으로 변환하는 조건자 함수는 현재 세션에 대해 SET DATEFORMAT 옵션을 기반으로 여러 행을 필터링할 수 있습니다.For example, a predicate function that implicitly converts a string to datetime could filter different rows based on the SET DATEFORMAT option for the current session. 일반적으로 조건자 함수는 다음과 같은 규칙을 준수해야 합니다.In general, predicate functions should abide by the following rules:

  • 조건자 함수는 문자열을 암시적으로 date, smalldatetime, datetime, datetime2 또는 datetimeoffset으로 변환하거나 그 반대로 변환하지 않아야 합니다. 이러한 변환은 SET DATEFORMAT(Transact-SQL)SET LANGUAGE(Transact-SQL) 옵션의 영향을 받기 때문입니다.Predicate functions should not implicitly convert character strings to date, smalldatetime, datetime, datetime2, or datetimeoffset, or vice versa, because these conversions are affected by the SET DATEFORMAT (Transact-SQL) and SET LANGUAGE (Transact-SQL) options. 대신 CONVERT 함수를 사용하여 스타일 매개 변수를 명시적으로 지정합니다.Instead, use the CONVERT function and explicitly specify the style parameter.

  • 조건자 함수는 주의 첫 번째 요일 값에 의존하지 않아야 합니다. 이 값은 SET DATEFIRST(Transact-SQL) 옵션의 영향을 받기 때문입니다.Predicate functions should not rely on the value of the first day of the week, because this value is affected by the SET DATEFIRST (Transact-SQL) option.

  • 조건자 함수는 오류(예: 오버플로 또는 0으로 나누기)가 발생한 경우 NULL을 반환하는 산술 또는 집계 식을 사용하지 않아야 합니다. 이 동작은 SET ANSI_WARNINGS(Transact-SQL), SET NUMERIC_ROUNDABORT(Transact-SQL)SET ARITHABORT(Transact-SQL) 옵션의 영향을 받기 때문입니다.Predicate functions should not rely on arithmetic or aggregation expressions returning NULL in case of error (such as overflow or divide-by-zero), because this behavior is affected by the SET ANSI_WARNINGS (Transact-SQL), SET NUMERIC_ROUNDABORT (Transact-SQL), and SET ARITHABORT (Transact-SQL) options.

  • 조건자 함수는 연결된 문자열을 NULL과 비교하지 않아야 합니다. 이 동작은 SET CONCAT_NULL_YIELDS_NULL(Transact-SQL) 옵션의 영향을 받기 때문입니다.Predicate functions should not compare concatenated strings with NULL, because this behavior is affected by the SET CONCAT_NULL_YIELDS_NULL (Transact-SQL) option.

보안 정보: 사이드 채널 공격 Security Note: Side-Channel Attacks

악의적인 보안 정책 관리자: 중요한 열의 상단에 보안 정책을 만들 수 있는 권한과 인라인 테이블 반환 함수를 만들거나 변경할 수 있는 권한을 가지고, 데이터를 추론하기 위해 부채널 공격을 사용하도록 설계된 인라인 테이블 반환 함수를 악의적으로 만들어 데이터를 유출할 목적으로 테이블의 선택 권한을 가진 다른 사용자와 공모할 수 있는 악의적인 보안 정책 관리자를 관찰하는 것이 중요합니다.Malicious security policy manager: It is important to observe that a malicious security policy manager, with sufficient permissions to create a security policy on top of a sensitive column and having permission to create or alter inline table valued functions, can collude with another user that has select permissions on a table to perform data exfiltration by maliciously creating inline table valued functions designed to use side channel attacks to infer data. 이러한 공격에는 공모(또는 악의적인 사용자에게 부여된 과도한 권한)가 필요하고, 정책(스키마 바인딩을 중단하기 위해 조건자를 제거하는 데 권한 필요) 및 인라인 테이블 반환 함수를 여러 번 수정이 필요하며, 대상 테이블에서 select 문의 반복 실행이 필요합니다.Such attacks would require collusion (or excessive permissions granted to a malicious user) and would likely require several iterations of modifying the policy (requiring permission to remove the predicate in order to break the schema binding), modifying the inline table valued functions, and repeatedly running select statements on the target table. 필요에 따라 권한을 제한하고 자주 변경되는 정책과 행 수준 보안과 관련된 인라인 테이블 반환 함수 등과 같은 의심스러운 활동을 모니터링하는 것을 매우 권장합니다.It is strongly recommended to limit permissions as it is necessary and to monitor for any suspicious activity such as constantly changing policies and inline table valued functions related to row-level security.

정교하게 만들어진 쿼리: 정교하게 만들어진 쿼리를 통해 정보가 누출될 수 있습니다.Carefully crafted queries: It is possible to cause information leakage through the use of carefully crafted queries. 예를 들어, 악의적인 사용자가 SELECT 1/(SALARY-100000) FROM PAYROLL WHERE NAME='John Doe' 을(를) 통해 John Doe의 급료가 $100,000임을 알게 됩니다.For example, SELECT 1/(SALARY-100000) FROM PAYROLL WHERE NAME='John Doe' would let a malicious user know that John Doe's salary is $100,000. 악의적인 사용자가 타인의 급여를 직접 쿼리하는 것을 방지하기 위해 보안 조건자가 있더라도, 해당 사용자는 쿼리가 0으로 나누기 예외를 반환할 때 알아낼 수 있습니다.Even though there is a security predicate in place to prevent a malicious user from directly querying other people's salary, the user can determine when the query returns a divide-by-zero exception.

기능 간 호환성 Cross-Feature Compatibility

일반적으로 행 수준 보안은 기능 간에 예상대로 작동합니다.In general, row-level security will work as expected across features. 그러나 몇 가지 예외가 있습니다.However, there are a few exceptions. 이 섹션에서는 SQL ServerSQL Server의 특정 다른 기능과 함께 행 수준 보안을 사용할 대의 몇 가지 주의 사항을 설명합니다.This section documents several notes and caveats for using row-level security with certain other features of SQL ServerSQL Server.

  • DBCC SHOW_STATISTICS 는 필터링되지 않은 데이터에 대한 통계치를 보고하기 때문에 보안 정책에 의해 보호되더라도 정보가 누출될 수 있습니다.DBCC SHOW_STATISTICS reports statistics on unfiltered data, and thus can leak information otherwise protected by a security policy. 따라서 행 수준 보안 정책이 적용되는 테이블에 대한 통계 개체를 보려면 테이블의 소유자이거나 sysadmin 고정 서버 역할, db_owner 고정 데이터베이스 역할 또는 db_ddladmin 고정 데이터베이스 역할의 멤버여야 합니다.For this reason, in order to view a statistics object for a table with a row-level security policy, the user must own the table or the user must be a member of the sysadmin fixed server role, the db_owner fixed database role, or the db_ddladmin fixed database role.

  • Filestream RLS는 Filestream과 호환되지 않습니다.Filestream RLS is incompatible with Filestream.

  • Polybase RLS는 Polybase와 호환되지 않습니다.Polybase RLS is incompatible with Polybase.

  • 메모리 액세스에 최적화된 테이블메모리에 최적화된 테이블에서 보안 조건자로 사용되는 인라인 테이블 반환 함수는 WITH NATIVE_COMPILATION 옵션을 사용하여 정의해야 합니다.Memory-Optimized TablesThe inline table-valued function used as a security predicate on a memory-optimized table must be defined using the WITH NATIVE_COMPILATION option. 이 옵션을 사용하면 메모리에 최적화된 테이블에서 지원되지 않는 언어 기능이 차단되며 만들 때 해당 오류가 발생합니다.With this option, language features not supported by memory-optimized tables will be banned and the appropriate error will be issued at creation time. 자세한 내용은 메모리 액세스에 최적화된 테이블 소개메모리 액세스에 최적화된 테이블의 행 수준 보안섹션을 참조하세요.For more information, see the Row-Level Security in Memory Optimized Tables section in Introduction to Memory-Optimized Tables.

  • 인덱싱된 뷰 일반적으로 보안 정책은 뷰를 기반으로 만들 수 있으며 뷰는 보안 정책에 의해 바인딩된 테이블을 기반으로 만들 수 있습니다.Indexed views In general, security policies can be created on top of views, and views can be created on top of tables that are bound by security policies. 그러나 인덱스를 통한 행 조회는 정책을 무시하기 때문에 보안 정책이 있는 테이블을 기반으로 인덱싱된 뷰를 만들 수는 없습니다.However, indexed views cannot be created on top of tables that have a security policy, because row lookups via the index would bypass the policy.

  • 변경 데이터 캡처 변경 데이터 캡처는 테이블에 CDC를 사용하도록 설정할 때 지정된 "제어" 역할의 멤버인 사용자 또는 db_owner 의 멤버로 필터링해야 하는 전체 행을 누출할 수 있습니다(참고: 모든 사용자가 변경 데이터에 액세스할 수 있도록 이를 명시적으로 NULL 로 설정할 수 있음).Change Data Capture Change Data Capture can leak entire rows that should be filtered to members of db_owner or users who are members of the "gating" role specified when CDC is enabled for a table (note: you can explicitly set this to NULL to enable all users to access the change data). 실제로 db_owner 및 이 제어 역할의 멤버는 테이블에 대한 보안 정책이 있는 경우에도 테이블의 모든 데이터 변경 내용을 볼 수 있습니다.In effect, db_owner and members of this gating role can see all data changes on a table, even if there is a security policy on the table.

  • 변경 내용 추적 변경 내용 추적은 SELECTVIEW CHANGE TRACKING 권한이 있는 사용자로 필터링해야 하는 행의 기본 키를 누출할 수 있습니다.Change Tracking Change Tracking can leak the primary key of rows that should be filtered to users with both SELECT and VIEW CHANGE TRACKING permissions. 실제 데이터 값은 누출되지 않습니다. 기본 키가 B인 행에 대해 열 A가 업데이트/삽입/삭제되었다는 사실만 누출됩니다.Actual data values are not leaked; only the fact that column A was updated/inserted/deleted for the row with B primary key. 이는 기본 키에 주민 등록 번호와 같은 기밀 요소가 포함된 경우 문제가 될 수 있습니다.This is problematic if the primary key contains a confidential element, such as a Social Security Number. 그러나 실제로 이 CHANGETABLE 은 최신 데이터를 가져오기 위해 거의 항상 원래 테이블과 조인됩니다.However, in practice, this CHANGETABLE is almost always joined with the original table in order to get the latest data.

  • 전체 텍스트 검색 CONTAINSTABLE, FREETEXTTABLE, semantickeyphrasetable, semanticsimilaritydetailstable, semanticsimilaritytable 등의 전체 텍스트 검색 및 의미 체계 검색 함수를 사용하는 쿼리는 행 수준 보안을 적용하고 필터링해야 하는 행의 기본 키 누출을 방지하기 위해 추가 조인이 도입되므로 성능 저하가 예상됩니다.Full-Text Search A performance hit is expected for queries using the following Full-Text Search and Semantic Search functions, because of an extra join introduced to apply row-level security and avoid leaking the primary keys of rows that should be filtered: CONTAINSTABLE, FREETEXTTABLE, semantickeyphrasetable, semanticsimilaritydetailstable, semanticsimilaritytable.

  • Columnstore 인덱스 RLS는 클러스터형 columnstore 인덱스 및 비클러스터형 columnstore 인덱스 모두와 호환됩니다.Columnstore Indexes RLS is compatible with both clustered and non-clustered columnstore indexes. 그러나 행 수준 보안이 함수에 적용되기 때문에 최적화 프로그램에서 배치 모드를 사용하지 않도록 쿼리 계획을 수정할 수 있습니다.However, because row-level security applies a function, it is possible that the optimizer may modify the query plan such that it does not use batch mode.

  • 분할 뷰 분할 뷰에는 차단 조건자를 정의할 수 없으며, 차단 조건자를 사용하는 테이블을 기반으로 분할 뷰를 만들 수 없습니다.Partitioned Views Block predicates cannot be defined on partitioned views, and partitioned views cannot be created on top of tables that use block predicates. 필터 조건자는 분할 뷰와 호환됩니다.Filter predicates are compatible with partitioned views.

  • 임시 테이블 은 RLS와 호환됩니다.Temporal tables are compatible with RLS. 그러나 현재 테이블의 보안 조건자는 기록 테이블에 자동으로 복제되지 않습니다.However, security predicates on the current table are not automatically replicated to the history table. 현재 및 기록 테이블 모두에 보안 정책을 적용하려면 각 테이블에서 개별적으로 보안 조건자를 추가해야 합니다.To apply a security policy to both the current and the history tables, you must individually add a security predicate on each table.

Examples

1. A. 데이터베이스에 인증하는 사용자에 대한 시나리오Scenario for users who authenticate to the database

이 간단한 예에서는 3명의 사용자를 만들고 6개 행의 테이블을 만들어 채운 다음, 인라인 테이블 반환 함수와 테이블에 대한 보안 정책을 만듭니다.This short example creates three users, creates and populates a table with 6 rows, then creates an inline table valued function and a security policy for the table. 예에서는 select 문이 다양한 사용자를 필터링하는 방법을 보여줍니다.The example shows how select statements are filtered for the various users.

서로 다른 액세스 기능을 보여주는 3개의 사용자 계정을 만듭니다.Create three user accounts that will demonstrate different access capabilities.

CREATE USER Manager WITHOUT LOGIN;  
CREATE USER Sales1 WITHOUT LOGIN;  
CREATE USER Sales2 WITHOUT LOGIN;  

데이터를 보유하는 간단한 테이블을 만듭니다.Create a simple table to hold data.

CREATE TABLE Sales  
    (  
    OrderID int,  
    SalesRep sysname,  
    Product varchar(10),  
    Qty int  
    );  

각 영업 담당자별로 3개의 주문을 보여주는 6행의 데이터로 테이블을 채웁니다.Populate the table with 6 rows of data, showing 3 orders for each sales representative.

INSERT Sales VALUES   
(1, 'Sales1', 'Valve', 5),   
(2, 'Sales1', 'Wheel', 2),   
(3, 'Sales1', 'Valve', 4),  
(4, 'Sales2', 'Bracket', 2),   
(5, 'Sales2', 'Wheel', 5),   
(6, 'Sales2', 'Seat', 5);  
-- View the 6 rows in the table  
SELECT * FROM Sales;  

각 사용자에게 테이블에 읽기 액세스 권한을 부여합니다.Grant read access on the table to each of the users.

GRANT SELECT ON Sales TO Manager;  
GRANT SELECT ON Sales TO Sales1;  
GRANT SELECT ON Sales TO Sales2;  

새로운 스키마 및 인라인 테이블 반환 함수를 만듭니다.Create a new schema, and an inline table valued function. SalesRep 열이 사용자가 실행한 쿼리와 동일한 경우(@SalesRep = USER_NAME()) 또는 쿼리를 실행한 사용자가 Manager 사용자(USER_NAME() = 'Manager')인 경우, 함수는 1을 반환합니다.The function returns 1 when a row in the SalesRep column is the same as the user executing the query (@SalesRep = USER_NAME()) or if the user executing the query is the Manager user (USER_NAME() = 'Manager').

CREATE SCHEMA Security;  
GO  

CREATE FUNCTION Security.fn_securitypredicate(@SalesRep AS sysname)  
    RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN SELECT 1 AS fn_securitypredicate_result   
WHERE @SalesRep = USER_NAME() OR USER_NAME() = 'Manager';  

필터 조건자로 함수를 추가하는 보안 정책을 만듭니다.Create a security policy adding the function as a filter predicate. 정책을 활성화하려면 상태는 ON으로 설정해야 합니다.The state must be set to ON to enable the policy.

CREATE SECURITY POLICY SalesFilter  
ADD FILTER PREDICATE Security.fn_securitypredicate(SalesRep)   
ON dbo.Sales  
WITH (STATE = ON);  

이제 각 사용자로 Sales 테이블에서 선택하여 필터링 조건자를 테스트합니다.Now test the filtering predicate, by selected from the Sales table as each user.

EXECUTE AS USER = 'Sales1';  
SELECT * FROM Sales;   
REVERT;  

EXECUTE AS USER = 'Sales2';  
SELECT * FROM Sales;   
REVERT;  

EXECUTE AS USER = 'Manager';  
SELECT * FROM Sales;   
REVERT;  

관리자는 6행 모두를 볼 수 있습니다.The Manager should see all 6 rows. Sales1 및 Sales2 사용자는 자신의 판매 행만 볼 수 있습니다.The Sales1 and Sales2 users should only see their own sales.

정책을 사용하지 않도록 보안 정책을 변경합니다.Alter the security policy to disable the policy.

ALTER SECURITY POLICY SalesFilter  
WITH (STATE = OFF);  

이제 Sales1 및 Sales2 사용자가 6 행 모두를 볼 수 있게 됩니다.Now the Sales1 and Sales2 users can see all 6 rows.

2. B. 중간 계층 응용 프로그램을 통해 데이터베이스에 연결하는 사용자에 대한 시나리오Scenario for users who connect to the database through a middle-tier application

이 예는 중간 계층 응용 프로그램이 응용 프로그램 사용자(또는 테넌트)가 동일한 SQL ServerSQL Server 사용자(응용 프로그램)을 공유하는 연결 필터링을 구현하는 방법을 보여줍니다.This example shows how a middle-tier application can implement connection filtering, where application users (or tenants) share the same SQL ServerSQL Server user (the application). 데이터베이스에 연결한 후 응용 프로그램이 SESSION_CONTEXT(Transact-SQL) 에서 현재 응용 프로그램 사용자 ID를 설정하면 보안 정책이 이 ID에 표시되지 않아야 하는 행을 투명하게 필터링하고 사용자가 잘못된 사용자 ID에 대한 행을 삽입하지 못하도록 차단합니다.The application sets the current application user ID in SESSION_CONTEXT (Transact-SQL) after connecting to the database, and then security policies transparently filter rows that shouldn't be visible to this ID, and also block the user from inserting rows for the wrong user ID. 다른 응용 프로그램은 변경하지 않아도 됩니다.No other app changes are necessary .

데이터를 보유하는 간단한 테이블을 만듭니다.Create a simple table to hold data.

CREATE TABLE Sales (  
    OrderId int,  
    AppUserId int,  
    Product varchar(10),  
    Qty int  
);  

각 응용 프로그램 사용자별로 3개의 주문을 보여주는 6행의 데이터로 테이블을 채웁니다.Populate the table with 6 rows of data, showing 3 orders for each application user.

INSERT Sales VALUES   
    (1, 1, 'Valve', 5),   
    (2, 1, 'Wheel', 2),   
    (3, 1, 'Valve', 4),  
    (4, 2, 'Bracket', 2),   
    (5, 2, 'Wheel', 5),   
    (6, 2, 'Seat', 5);  

응용 프로그램에서 연결하는 데 사용할 낮은 권한의 사용자를 만듭니다.Create a low-privileged user that the application will use to connect.

-- Without login only for demo  
CREATE USER AppUser WITHOUT LOGIN;   
GRANT SELECT, INSERT, UPDATE, DELETE ON Sales TO AppUser;  

-- Never allow updates on this column  
DENY UPDATE ON Sales(AppUserId) TO AppUser;  

행을 필터링하는 SESSION_CONTEXT 에 저장된 응용 프로그램 사용자 ID를 사용할 새로운 스키마 및 조건자 함수를 만듭니다.Create a new schema and predicate function, which will use the application user ID stored in SESSION_CONTEXT to filter rows.

CREATE SCHEMA Security;  
GO  

CREATE FUNCTION Security.fn_securitypredicate(@AppUserId int)  
    RETURNS TABLE  
    WITH SCHEMABINDING  
AS  
    RETURN SELECT 1 AS fn_securitypredicate_result  
    WHERE  
        DATABASE_PRINCIPAL_ID() = DATABASE_PRINCIPAL_ID('AppUser')    
        AND CAST(SESSION_CONTEXT(N'UserId') AS int) = @AppUserId;   
GO  

Sales에 대한 필터 조건자 및 차단 조건자로서 이 함수를 추가하는 보안 정책을 만듭니다.Create a security policy that adds this function as a filter predicate and a block predicate on Sales. 차단 조건자에는 AFTER INSERT만 필요합니다. BEFORE UPDATEBEFORE DELETE 는 이미 필터링되었고 AFTER UPDATE 는 이전에 설정된 열 권한으로 인해 AppUserId 열을 다른 값으로 업데이트할 수 없어 필요 없기 때문입니다.The block predicate only needs AFTER INSERT, because BEFORE UPDATE and BEFORE DELETE are already filtered, and AFTER UPDATE is unnecessary because the AppUserId column cannot be updated to other values, due to the column permission set earlier.

CREATE SECURITY POLICY Security.SalesFilter  
    ADD FILTER PREDICATE Security.fn_securitypredicate(AppUserId)   
        ON dbo.Sales,  
    ADD BLOCK PREDICATE Security.fn_securitypredicate(AppUserId)   
        ON dbo.Sales AFTER INSERT   
    WITH (STATE = ON);  

이제 Sales SESSION_CONTEXT 에서 다른 응용 프로그램 사용자 ID를 설정한 후테이블에서 선택하여 연결 필터링을 시뮬레이트할 수 있습니다.Now we can simulate the connection filtering by selecting from the Sales table after setting different user IDs in SESSION_CONTEXT. 실제로 응용 프로그램은 연결을 연 후 SESSION_CONTEXT 에서 현재 사용자 ID를 설정합니다.In practice, the application is responsible for setting the current user ID in SESSION_CONTEXT after opening a connection.

EXECUTE AS USER = 'AppUser';  
EXEC sp_set_session_context @key=N'UserId', @value=1;  
SELECT * FROM Sales;  
GO  

--  Note: @read_only prevents the value from changing again   
--  until the connection is closed (returned to the connection pool)  
EXEC sp_set_session_context @key=N'UserId', @value=2, @read_only=1;   

SELECT * FROM Sales;  
GO  

INSERT INTO Sales VALUES (7, 1, 'Seat', 12); -- error: blocked from inserting row for the wrong user ID  
GO  

REVERT;  
GO  

참고 항목See Also

CREATE SECURITY POLICY(Transact-SQL) CREATE SECURITY POLICY (Transact-SQL)
ALTER SECURITY POLICY(Transact-SQL) ALTER SECURITY POLICY (Transact-SQL)
DROP SECURITY POLICY(Transact-SQL) DROP SECURITY POLICY (Transact-SQL)
CREATE FUNCTION(Transact-SQL) CREATE FUNCTION (Transact-SQL)
SESSION_CONTEXT(Transact-SQL) SESSION_CONTEXT (Transact-SQL)
sp_set_session_context(Transact-SQL) sp_set_session_context (Transact-SQL)
sys.security_policies(Transact-SQL) sys.security_policies (Transact-SQL)
sys.security_predicates(Transact-SQL) sys.security_predicates (Transact-SQL)
사용자 정의 함수 만들기(데이터베이스 엔진)Create User-defined Functions (Database Engine)