Dynamic data masking, explained
Updated:
Dynamic data masking obscures sensitive values in a query result while
leaving the stored data untouched. The row still comes back; the personal data
in it does not. With QueryProxy’s partial strategy, an email column returns
a***@***.com and a 16-digit card number returns 4********2 (first and last
character kept), and the developer sees the shape of the data without receiving
the data itself.
The word doing the work is dynamic. Nothing in the database changes. The transformation happens on the way out, per query.
Why results are the leak
Access control usually stops at “may this person read this table?”, and a correctly authorised read is the most common way personal data leaves a company. Nobody breaks in. An engineer with a legitimate reason runs a legitimate query, and ten thousand real email addresses land in a terminal, a scrollback buffer, a CSV, and eventually a chat thread.
Two kinds of rule
Column patterns match on the name: email, phone, ssn, credit_card.
Cheap, predictable and easy to audit, but blind to a column called contact_1.
Content regexes match on the value, catching an email address wherever it
appears — including inside a free-text notes field where nobody thought to look.
Broader reach, more cost, and a real risk of false positives.
Neither is sufficient alone. Column rules cover the schema you designed; content rules cover the schema you actually have.
Masking at write time
This is the detail that decides whether masking is real.
If results are stored unmasked and masked at display time, an unmasked copy exists — in storage, in backups, in whatever else reads that file. The mask is a rendering choice, and every layer beneath it holds the real thing.
If masking runs inside the result stream, before rows are written, the unmasked version of a matching value is never written to the result store. There is nothing to leak from storage afterwards because nothing was kept there. Masking applies where a rule matches: a column or value no rule covers comes back as it is.
QueryProxy masks at write time, as rows stream from the cursor to storage.
Where masking is the wrong tool
When the real value is the requirement. Debugging one customer’s specific broken record needs that record. The answer is an explicit, audited exception — not weakening the default for everyone.
As a substitute for access control. Masking limits what a permitted query returns. It has nothing to say about who may run one.
For analytics on the masked output. Masked data is deliberately lossy. Aggregate over it and you will get confidently wrong answers.
Further reading
The data masking documentation covers rule configuration and the three strategies QueryProxy ships with. For how masking fits alongside approvals, see what database access control is. Per-engine notes are on the PostgreSQL and MySQL and MariaDB pages. For how masking relates to personal data and card numbers, see GDPR and database access and PCI DSS and database access controls.
Frequently asked
- What is dynamic data masking?
- Dynamic data masking replaces sensitive values in a query result with obscured ones while leaving the stored data untouched. An email column might come back as a***@***.com, so the row remains useful for debugging while the personal data does not leave with it.
- How is masking different from encryption?
- Encryption is reversible with a key and protects data at rest or in transit. Masking is deliberately one-way and protects data in use — a masked value cannot be turned back into the original, which is exactly why it is safe to show to someone who should not see the real one.
- Should masking happen before or after results are stored?
- Before. If results are written to disk unmasked and masked only when displayed, an unmasked copy exists in storage, in backups and in anything that reads that file. Masking inside the result stream means the unmasked version is never written to the result store.
- Does masking make results useless for debugging?
- Usually not, because most debugging needs the shape of the data rather than the values — which rows exist, which fields are null, how records relate. Masking preserves that structure. When an engineer genuinely needs a real value, that is a request that deserves an explicit decision rather than a default.