Access Control for Analytical Data
Why role-based access breaks down on a data platform, how attribute-based policies and dynamic masking replace it, and the derived-table hole that undermines both.
Access control in a transactional system is tractable: a bounded set of resources, a bounded set of operations, and roles that map cleanly onto job functions. A data platform has tens of thousands of tables, new ones daily, columns of wildly different sensitivity in the same table, and users who need row-level slices rather than whole objects. Role-based access control does not scale to it, and every mature platform has moved to something else.
Why roles explode
The natural role is a cross product: a data scientist in the EU marketing team needs EU customer rows, non-sensitive columns, in marketing tables. Enumerate the combinations of region, team, sensitivity level and dataset and the role count grows multiplicatively. Real platforms end up with thousands of roles, nobody knows what any of them grant, and the practical outcome is over-provisioning, because a broad role is easier to approve than to decompose.
Attribute-based policies
The alternative expresses access as a rule over attributes of the user, the resource and the context, evaluated at query time.
A policy states that a user may see rows where the row's region matches the user's region, and may see columns whose classification is at or below the user's clearance. One policy covers every table with those attributes, and adding a table requires tagging it rather than creating roles.
Two mechanisms implement it in practice:
Row-level security attaches a predicate to a table that the engine appends to every query, so a query returning all rows returns only the permitted ones, transparently to the user.
Dynamic masking replaces a column's value at read time based on the reader's attributes: full value for an authorised user, a hash or a redaction or a partial value for others. This is what makes it safe to leave sensitive columns in tables that many people query, since the protection travels with the column rather than living in a separate restricted copy.
Both depend on classification metadata being present and correct, which is why catalogue tagging becomes load-bearing infrastructure rather than documentation.
When it breaks
Derived tables lose their protections. A user with access to a masked column creates a table from it, and the new table has no policy attached. The classification must propagate through lineage, automatically, or the entire control is one CREATE TABLE AS SELECT away from being bypassed. This is the most common structural hole in data access control.
Aggregates leak individuals. A user permitted to see counts but not rows can often reconstruct individual values by differencing queries: count where region equals X, then count where region equals X and age exceeds 40. Preventing this requires query auditing, minimum aggregation thresholds, or formal differential privacy, and most platforms do none of them.
Policy evaluation costs query time. Row-level predicates and masking functions run per row, and a complex policy on a large scan is measurable. Policies should be expressible as simple predicates over partitioned or clustered columns so the engine can prune, and a policy that forces a full scan has turned a control into a performance problem people will route around.
Emergency access needs a designed path. Incidents require someone to see data they normally cannot. Without a break-glass mechanism that is audited and time-bounded, teams create permanent exceptions during the first incident and never remove them.
Access control does not survive export. Once a user downloads a result set, no policy applies to the copy. Controls on the query path must be paired with controls on egress, and platforms that harden the first while leaving the second open have moved the risk rather than reduced it.
12 flashcards for this concept
Click a card to reveal the answer.