Chapter 22 · Security, Privacy, Row/Column Policies, Masking, and Least-Privilege Analytics

Row-Level/Column-Level Security, Dynamic Masking, Tokenization, and Policy Placement

Apply row and column policy semantics, masking, tokenization, and policy placement to synthetic customer data while proving that masking alone is not authorization and that raw access can bypass a secure view.

Intermediate → Advanced150–185 minutesRow/column policy + masking/token lab3 consented marketing rows · raw bypass repairedLast reviewed: September 2026

Learning outcomes

01

Distinguish row filtering, column denial, masking, and tokenization as different controls.

02

Explain why masking changes representation but does not necessarily remove underlying privilege.

03

Implement a deterministic secure customer surface with purpose-scoped rows and columns.

04

Detect a direct-table bypass that defeats a correctly masked BI view.

05

Place policy close enough to governed data that alternate query routes cannot silently avoid it.

Continuity and explicit security-layer addition

Chapter 22 begins from Chapter 21's certified warehouse/domain truth: 10 current paid lines, 8 orders, 12 units, 820 USD gross revenue, 495 USD cost, and 325 USD gross profit. The canonical fact grain remains one current paid order line. The governed metrics and conformed dimensions are not redefined. This chapter adds identity, authorization, privacy, copy-control, and audit mechanisms around those same assets. A security control that changes revenue merely because a different user queried it is a modeling bug unless the metric contract explicitly defines a security-scoped population.

Lab contract and limits

Runtime executed for this chapter: Python 3.13.5 and SQLite 3.46.1. Environment: local/in-process, synthetic, no-cost. Storage: one disposable SQLite file plus JSON evidence. Security engine: SQLite does not provide a warehouse-style role/RLS/CLS subsystem, so the lab implements a deterministic policy evaluator and secure-view simulation while keeping the policy semantics vendor-neutral; PostgreSQL/cloud-warehouse row policies are referenced only as product-specific examples. Token key: supplied at runtime through ATLASMART_TOKEN_KEY; no production secret is embedded in SQL or source. Time/currency: existing UTC and USD metric semantics remain unchanged. Privacy: all names/emails are synthetic. The erasure workflow is a technical teaching model, not legal advice or a universal retention rule.

1. The problem: the secure view masks email, but the analyst can still query raw

Marketing needs customer segments and geography for campaign analysis. It does not need clear-text names or emails. A row-level policy decides which records are visible; a column-level policy decides which attributes are accessible. Masking returns a transformed representation, such as a***@example.test. Tokenization replaces an identifier with a stable surrogate whose mapping/key is separately protected. These controls solve different problems.

The declared consumer is marketing analytics. The relevant customer state includes marketing consent. The failure mode is subtle: a secure view can be perfectly designed and still useless if the same role retains direct access to the underlying restricted table.

2. Row and column policy are not synonyms

Control AtlasMart example What it does not guarantee
Row policy Marketing surface includes only marketing_consent=1 Does not hide email on allowed rows
Column policy Direct customer_name/email unavailable Does not restrict which customers appear
Dynamic masking ada@example.test → a***@example.test Does not revoke raw-table privilege
Tokenization D-CUST-001 → tok_… Not automatic anonymization; mapping/key still matters
Policy intent (not native SQLite role syntax)
-- Product-neutral policy intent; NOT executed in SQLite as native RLS/roles.-- Exact syntax varies by warehouse.ROLE marketing_analyst:  ALLOW SELECT secure_customer_view  DENY  SELECT raw_customer  COLUMNS token, segment, geography_code, masked_email  ROWS marketing_consent = trueROLE privacy_operator:  ALLOW SELECT_PII, ERASE_PII ON dim_customer/raw_customer  DENY  ADMIN ON security_catalogROLE pipeline_writer (service identity):  ALLOW INSERT raw_customer, UPSERT dim_customer  DENY  BI export and security administration

3. Executed secure-view result

Observed masked/policy-filtered rows
marketing_campaign_analysis secure rowscustomer_token              segment      geography   masked_emailtok_eb060e042b673d546dc8    Mid-Market   G-NORTH     a***@example.testtok_e59219f7c6b135fc7599    Consumer     G-EAST      b***@example.testtok_b3c2ecea1fc59fbca6d1    Enterprise   G-EAST      c***@example.testD-CUST-004 is excluded because marketing_consent=0.D-CUST-005 is excluded because marketing_consent=0.

The lab has five customer identities. Three consented synthetic customers are visible to the marketing policy; C004 and the inferred C005 are excluded. Clear names are absent. The token is stable for the lab key, which enables joins across approved datasets without exposing the original durable ID to the consumer.

4. Tokenization is pseudonymization, not a magic deletion primitive

The token is an HMAC of the durable customer ID using a runtime key. If someone gains both the protected mapping/key and surrounding data, the token may remain linkable. Treating it as anonymous simply because it looks random is unsafe. The lab stores a token_vault mapping for approved internal use, then deletes the C004 mapping during the privacy workflow.

Executed token function
def token(durable_id):    return 'tok_'+hmac.new(KEY.encode(),durable_id.encode(),hashlib.sha256).hexdigest()[:20]# KEY comes from ATLASMART_TOKEN_KEY; it is not embedded in source.

5. Controlled failure: masking works, authorization still fails

Before repair
alice.marketing -> prod.secure_customer_view SELECT = ALLOWreturned email = a***@example.testalice.marketing -> prod.raw_customer SELECT = ALLOWraw row contains clear customer_name + emailsecurity conclusion = BYPASS FOUND

This is why a masked query result is not evidence that the user cannot retrieve the unmasked value elsewhere. The repair removes the analytical role's direct raw grant and tests the effective permission again.

Executed repair
c.execute("DELETE FROM permission WHERE role='marketing_analyst' AND resource='prod.raw_customer' AND action='SELECT'")assert authorize(c,'alice.marketing','prod.raw_customer','SELECT')[0]=='DENY'

6. Where should policy live?

Policy can exist in a warehouse table policy, secure view, semantic layer, BI tool, API, or export process. The closer the enforcement is to the governed data boundary, the fewer alternate routes exist. Higher layers may still add purpose-aware UX, but they should not be the only barrier when users can query lower layers. A production system may also need object-storage policies, staging schemas, notebook credentials, and service-account restrictions. The exact feature names and syntax vary by engine.

7. Performance and query semantics

Row filters and masking expressions can change plans or prevent some optimizations in certain products, but this chapter reports no invented overhead. Benchmark the same authorized result under representative scale/cache/concurrency before tuning. Never weaken policy merely to preserve a benchmark number without an explicit risk decision.

8. Verification checklist

  1. Confirm marketing returns three policy-approved rows.
  2. Confirm no clear customer_name is returned.
  3. Confirm masked emails are deterministic for the fixture.
  4. Confirm the initial raw bypass is detected.
  5. Remove the raw grant and assert direct raw SELECT becomes DENY.
  6. Verify finance and ETL roles still retain only their required actions.

9. Production judgment and rollback

Use row policies for population boundaries, column policy for attribute boundaries, masking for controlled presentation, and tokens for controlled linkage. None substitutes for privilege review. Version policy definitions, test them with named identities, and keep a rollback route that restores the previous reviewed policy—not a blanket grant. Chapter 3's dimensional keys and Chapter 20's metric semantics remain unchanged.

10. Bridge to Lesson 3

Row/column controls answer what an authorized consumer can see. Lesson 3 asks a different question: what personal data should exist at all, for what purpose, for how long, under what consent/legal policy, and what happens when a verified deletion request meets historical facts, raw evidence, exports, and backups.

Knowledge check

Acceptance questions

  1. Why is masking not equivalent to authorization?
  2. How does row security differ from column security?
  3. Why is a token not automatically anonymous?
  4. Why should a bypass test query lower layers directly?
Review the answers

1. It changes returned representation; an alternate raw privilege can still expose the source value.

2. Row policy restricts records; column policy restricts attributes.

3. Re-identification may remain possible through keys/mappings/context.

4. Because intended BI policy is irrelevant if the same identity can avoid it.

Authoritative references

11. Lab cleanup/reset

Delete atlasmart_ch22_lab (or your configured disposable lab directory) and rerun ch22_lab.py with a synthetic ATLASMART_TOKEN_KEY. The lab modifies no repository, cloud, or production resource.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.