{"id":14791,"date":"2016-01-21T09:30:00","date_gmt":"2016-01-21T17:30:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/dataplatforminsider\/2016\/01\/21\/limiting-access-to-data-using-row-level-security\/"},"modified":"2024-01-22T22:52:25","modified_gmt":"2024-01-23T06:52:25","slug":"limiting-access-to-data-using-row-level-security","status":"publish","type":"post","link":"https:\/\/www.microsoft.com\/en-us\/sql-server\/blog\/2016\/01\/21\/limiting-access-to-data-using-row-level-security\/","title":{"rendered":"Limiting access to data using Row-Level Security"},"content":{"rendered":"
To satisfy compliance standards, internal regulations or basic security principles, applications often need to limit a user’s access to only certain rows of data in a database. For example:<\/p>\n
Traditionally, customers have implemented their row-level access logic using SQL views or customized application code. But these workarounds can introduce problems: Views are decentralized, susceptible to runtime errors and difficult to maintain during application upgrades. And customized application code is not only difficult to maintain as your codebase grows, but also impossible in scenarios where you don’t own the application code (e.g., commercial off-the-shelf software).<\/p>\n
Row-Level Security (RLS)<\/a>, a new programmability feature available in Azure SQL Database and SQL Server 2016, solves these problems by centralizing your row-level access logic within the database. As your application grows, RLS helps you maintain a consistent data access policy and reduce the risk of accidental data leakage.<\/p>\n <\/p>\n RLS is a form of “predicate-based access control” \u2014 it works by automatically applying a security predicate to all queries on a table. The predicate determines which users can access which rows. For example, a simple predicate might be, “WHERE SalesRep = CURRENT_USER”, while a complicated predicate might include JOINs to look up information in other tables.<\/p>\n There are two types of security predicates:<\/p>\n To add a security predicate on a table, you first need an inline table-valued function that defines your access criteria. Then, you create a security policy that adds filter and block predicates on any tables you like, using this function. Here’s a simple example that prevents sales representatives from accessing rows in a customer’s table that are not assigned to them:<\/p>\n CREATE FUNCTION<\/span> dbo.customerPredicate(<\/span>@SalesRepName AS sysname<\/span>)<\/span><\/span> CREATE SECURITY POLICY<\/span> dbo.customerAccessPolicy<\/span> — Now test the policy by impersonating SalesRep01<\/span> — Only rows where SalesRepName = ‘SalesRep01’ are returned (filter predicate)<\/span> — Error because the new SalesRepName <> ‘SalesRep01’ (block predicate)<\/span> REVERT<\/span> What is the performance impact of using RLS?<\/strong><\/em><\/p>\n In general, RLS will have the same performance as a view. Because RLS relies on the query optimizer to inline the predicate function efficiently, the performance depends on the complexity of your queries and predicates, as well as any indexes you have created. For more information, see Row-Level Security: Performance and common patterns<\/a>.<\/p>\n Can I limit access based on AD group memberships?<\/strong><\/em><\/p>\n Yes, you can use the IS_MEMBER()<\/a> function in your predicate to check SQL role or AD group memberships. For an example, see the RLS Hospital Demo script<\/a>.<\/p>\n What if my application uses connection pooling with a single login for all users?<\/strong><\/em><\/p>\n No problem, your application can use the new SESSION_CONTEXT<\/a> feature to get and set session-scoped key-value pairs to identify users for RLS, while still enabling efficient connection pooling. For examples, see the RLS Mid-Tier Demo script<\/a> or the Web app with a multitenant database using Entity Framework and Row-Level Security tutorial<\/a>.<\/p>\n If I’ve enabled RLS, does this mean that my DBAs cannot access data using SSMS?<\/strong><\/em><\/p>\n No. Like other row-level security solutions, RLS is intended for scenarios where the queries that a user can execute are controlled by a middle-tier application. DBAs and users with ad-hoc query access to the database may be able to infer the existence of filtered data, using side-channels. For more information, see the RLS official documentation<\/a>.<\/p>\n If your application needs to limit users’ access to specific rows of data, we encourage you to use RLS. The easiest way to try it with SQL Server 2016 is to download the AdventureWorks Database for SQL Server 2016 CTP3<\/a> and walk through the RLS sample script.<\/p>\n You can also learn more with the following resources:<\/p>\nHow it works<\/h1>\n
\n
\n\u00a0\u00a0\u00a0 RETURNS TABLE<\/span><\/span>
\n\u00a0\u00a0\u00a0 WITH SCHEMABINDING<\/span>
\nAS<\/span>
\n\u00a0\u00a0\u00a0 RETURN SELECT<\/span> 1 AS<\/span> accessResult <\/span>
\n\u00a0\u00a0\u00a0 WHERE<\/span> @SalesRepName = USER_NAME<\/span>()<\/span> OR USER_NAME<\/span>()<\/span> = ‘Manager’<\/span><\/span>
\ngo<\/span><\/p>\n
\n\u00a0\u00a0\u00a0 ADD FILTER PREDICATE<\/span> dbo.customerPredicate(<\/span>SalesRepName)<\/span> ON<\/span> dbo.Customers,<\/span>
\n\u00a0\u00a0\u00a0 ADD BLOCK PREDICATE<\/span> dbo.customerPredicate(<\/span>SalesRepName)<\/span> ON<\/span> dbo.Customers<\/span>
\ngo<\/span><\/p>\n
\nEXECUTE AS USER<\/span> = ‘SalesRep01’<\/span><\/span>
\ngo<\/span><\/p>\n
\nSELECT<\/span> * FROM<\/span> dbo.Customers<\/span>
\ngo<\/span><\/p>\n
\nINSERT INTO<\/span> dbo.Customers <\/span>
\n\u00a0\u00a0\u00a0 (<\/span>CustomerId,<\/span> CustomerName, SalesRepName) <\/span><\/span>
\nVALUES <\/span>
\n\u00a0\u00a0\u00a0 (<\/span>1,<\/span> ‘New Customer’<\/span>,<\/span> ‘SalesRep99’<\/span>)<\/span><\/span>
\ngo<\/span><\/p>\n
\ngo<\/span><\/p>\nFrequently asked questions<\/h1>\n
Getting started<\/h1>\n