Skip to content
About
Main content

Row- and column-level security

What does fine-grained access control cost, and why does row filtering not stop inference?

Until now the protected object was a catalog object (a whole table), so policy granularity equalled object granularity. But the real sensitivity boundary usually runs inside the table: rows that belong to other regions, columns that hold identifiers. There are two ways out. They are the same granularity-mismatch fork you meet everywhere.

Option 1: split the table physically, one object per policy class (visits_north, visits_south, ...). This is what everyone did for decades, and it works. The price: N times the maintenance work (compaction, schema changes, backfills), a data model cut along a dimension that has nothing to do with the data's meaning, and the policy hidden inside pipeline code where no access review will find it.

Option 2: keep one object and evaluate a predicate at read time. The policy becomes a filter such as region = current_user_region(), added to every query. It is cleaner in every way but one: the security predicate becomes an input to the query optimizer, and it inherits every optimizer problem.

  • Pruning. If the predicate does not match the partition or clustering key (how files are organized), the engine cannot skip files. A user who is entitled to 0.1% of rows still causes a full scan. This is a cost consequence (the 40 TB) created by a security decision.
  • Joins. If the predicate refers to a lookup table (for example, a table of which user may see which region), every query becomes a join.
  • Plan freedom leaks. The optimizer is allowed to reorder predicates. So a cheap user-written predicate such as WHERE 1/diagnosis_score > 0 can be evaluated before the security predicate. If it fails (division by zero) on a row the user may not see, the error message, the timing or a side effect reveals that the row exists. This is why engines have the idea of a (a boundary the optimizer may not move predicates across) and (functions proven not to reveal their input through errors). The general problem: freedom to choose a query plan and control of information flow are in direct conflict.

The boundary that matters most

A user who can compute aggregates over their filtered view, and who knows an unfiltered total from somewhere else, can subtract. If the whole hospital had 1,000,000 visits and the user's view returns 120,000, the user knows the other regions together have 880,000. Cell-level access control does not limit what can be learned. That limit is exactly what the privacy part of this mechanism (Privacy) deals with.

In one sentence: fine-grained access control turns a policy problem into a query-planning problem, and pays in pruning, plan freedom and inference leakage.