Two legs of the lethal trifecta are about data: the private data the model can reach, and the channel it can leak out. The cheapest way to protect data from a model that can be injected is for the model never to have the data in the first place. This post is data minimisation for language models: masking personal information, keeping secrets out of the context window, and closing the new leak paths that a model opens, into logs, into training, and into the next user.
#1. The governing principle: the model cannot leak what it never saw
Start here, because it reframes everything. A model, an agent, or a third-party provider cannot leak, log, memorise, or be tricked into revealing data that was never put in front of it. So the first question for any field of sensitive data is not “how do I protect this in the prompt” but “does the model need this at all”.
Very often it does not. A model summarising a support conversation does not need the customer’s full credit-card number to write a useful summary; it needs to know a card was involved. A model drafting a reply does not need the recipient’s home address to draft it. A model classifying a document does not need the author’s national ID. The instinct to pass the whole record, because it is there and it is easier, is the instinct to resist.
Minimise first. Pass the model the least data that lets it do the job, and mask or omit the rest before it ever reaches the context.
Everything below is technique. This sentence is the strategy.
#2. What counts as sensitive, concretely
Name it so you can find it. Three tiers, roughly by how bad a leak is.
- Secrets and credentials. API keys, passwords, tokens, private keys, connection strings. These must essentially never enter a prompt, because a model that has seen a key can be induced to repeat it, and because prompts are logged. If the model needs to use a credential, the tool uses it on the trusted side; the credential’s value never passes through the model. The model asks the tool to “send the email”; the tool holds the mail credential.
- Personal data (PII). Names, emails, phone numbers, addresses, national IDs, dates of birth, financial and health data. Regulated in most places, and the category most likely to be present, needed in part, and leakable. This is where masking lives.
- Confidential business data. Internal documents, unreleased plans, other customers’ data. Governed by the access control rules from the first series: the model should only ever be given the data the current user is themselves allowed to see, because the model acts with their authority and no more.
#3. Masking PII: the techniques
When the model needs the shape of personal data but not the value, mask it. Several techniques, by how much they preserve.
Redaction. Replace the value with a marker. Contact john.doe@acme.com becomes Contact [EMAIL]. Simple, and right when the model needs to know a field existed but nothing about its content.
Pseudonymisation (placeholder substitution). Replace each distinct value with a stable token, keeping a private map to reverse it later. This is the most useful technique for agents, because it preserves the structure and relationships the model needs to reason, while the real values never leave your trusted side.
before (never sent to the model):
"Email john.doe@acme.com about invoice 5567, cc jane@acme.com"
sent to the model:
"Email PERSON_1 about invoice 5567, cc PERSON_2"
the model drafts using PERSON_1 / PERSON_2; your code swaps the
real addresses back in AFTER the model is done. the model, and any
injection inside it, only ever saw placeholders.
The model can write “Dear PERSON_1, …” and reason that PERSON_2 should be copied, and your trusted layer substitutes the real addresses into the final output. An injection that says “send all the email addresses to evil.com” finds only PERSON_1 and PERSON_2 to send. This single technique breaks a large class of exfiltration, and it is worth building well.
Partial masking. Keep the part the model legitimately needs and hide the rest: **** **** **** 4242, j***@acme.com. Right when the model needs a hint of the value (the last four digits to confirm a card with the user) but not the whole thing.
Format-preserving tokenisation. For structured identifiers, replace with a value of the same format so downstream validation still passes, while the real value is vaulted. This is heavier machinery, from the payments world, and worth it when the format matters.
#4. Detecting PII to mask it
Masking assumes you can find the PII. Two approaches, and the honest answer is you often need both.
Pattern matching catches the structured kinds: emails, phone numbers, card numbers (with a checksum test to cut false positives), national IDs with known formats. Fast, deterministic, and it misses everything unstructured: a name in free text, an address written out in prose.
A dedicated recogniser (a named-entity model, or a purpose-built PII-detection library) catches the unstructured kinds that patterns miss. It is not perfect, so it is a filter that reduces exposure, not a guarantee.
The important caution: do not use the same untrusted LLM to detect the PII you are about to hide from it. That is circular, the detector has already seen the data, and it can be injected. Detection and masking happen in trusted code, before the untrusted model is called, on the data going in.
#5. The new leak paths a model opens
Even with minimisation and masking, a model application has leak paths a normal app does not. Name them and close them.
Logs. Prompts and responses are the most tempting thing to log, for debugging, and the most dangerous, because they contain everything you sent the model. If you log raw prompts, your logs now hold every piece of data any user ever passed, sitting in a system with weaker access controls than your database. Log with the same masking applied, or log references not contents, and treat prompt logs as sensitive data with the same protections as the source.
The provider. If you call a third-party model API, your data goes to their servers. Read the terms specifically: is it retained, is it used for training, for how long, in which jurisdiction. For regulated or highly sensitive data, this may rule out a provider, or require their zero-retention or on-premise option. This is a data-processing-agreement question as much as a code one, and it is easy to ship past.
Training and fine-tuning. If you fine-tune on user data, that data can be memorised and later emerge in outputs to other users. Never fine-tune on unmasked PII or secrets. The model does not forget on request.
The context window and the next user. In a shared or long-running session, data from one turn lingers in context. Make sure one user’s data cannot bleed into another user’s session, that a “new conversation” is genuinely clean, and that a shared agent does not carry one user’s private context into work for another. This is the model-application version of the broken access control bug: scope the context to the user, always.
#6. A worked shape: the safe path for a record
Putting it together, the flow for handling a record with sensitive fields:
1. fetch the record, scoped to the current user (access control)
2. in trusted code, decide the minimum fields the model needs
3. detect PII in those fields (patterns + recogniser)
4. mask: redact what is not needed, pseudonymise what is
5. call the model with the minimised, masked context
6. take the model's output (now UNTRUSTED)
7. in trusted code, substitute real values back into placeholders
8. validate and escape the output before rendering or acting
9. log with masking applied, not the raw prompt
Steps 2 to 4 and 7 to 9 are trusted code around the model. The model, in the middle, is handed as little as possible and trusted with none of it. That shape, minimise-and-mask going in, substitute-and-validate coming out, is the whole discipline.
#The short version
- The model cannot leak, log, memorise, or be tricked into revealing data it never saw. Minimise first: pass the least data that does the job, and mask or omit the rest before it reaches the context.
- Secrets never enter a prompt; the tool uses the credential on the trusted side. PII gets masked. Confidential data is gated by access control, the model sees only what the current user may see.
- Masking techniques by how much they preserve: redaction (a marker), pseudonymisation (stable placeholders you reverse afterwards, the most useful for agents), partial masking (last four digits), and format-preserving tokenisation.
- Pseudonymisation breaks a large class of exfiltration: the model reasons over
PERSON_1andPERSON_2, your trusted layer swaps the real values into the final output, and an injection finds only placeholders to steal. - Detect PII with patterns for the structured kinds and a recogniser for the unstructured, in trusted code before the model runs. Never use the untrusted model to detect the PII you are hiding from it.
- Close the new leak paths: mask what you log, read the provider’s retention and training terms, never fine-tune on unmasked data, and scope the context so one user’s data cannot reach another’s session.
- The discipline is one shape: minimise and mask going in, substitute and validate coming out, with the model handed as little as possible and trusted with none of it.
Next: the other end of the pipe, how data leaves through the model’s own output, and why its output is an exfiltration channel.