Data Loss Prevention (DLP)
The built-in DLP check (checks.body.dlp) scans message bodies for secrets and
PII and takes one of two actions per finding kind: block the message
outright, or redact the match in place and forward the rewritten body. It is
the only Shield check that mutates traffic rather than just deciding on it —
typically used to stop keys and credentials from leaving through API responses,
and to mask PII before it reaches downstream consumers or logs.
DLP is a built-in body check, not an engine — it runs inside the body_checks
stage (see Built-in Checks & Pipeline Order) and
requires body inspection on its direction
(see Body Inspection & Limits).
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
direction | string | response | Where DLP runs: response | request | both. |
block | string[] | empty | DLP kinds that cause a block. |
redact | string[] | empty | DLP kinds masked in place. |
At least one of block / redact is required.
With no direction set, DLP only scans responses. To scrub or block request
bodies (e.g. stop users pasting credentials into your API), set
direction: request or both — and enable body inspection for that direction.
The nine kinds
| Kind | What it matches | Redaction behavior |
|---|---|---|
credit_card | Payment card numbers (Luhn-validated) | Keeps the last 4 digits |
ssn | US Social Security numbers | [REDACTED:ssn] |
email | Email addresses | [REDACTED:email] |
jwt | JSON Web Tokens | [REDACTED:jwt] |
aws_access_key | AWS access key IDs | [REDACTED:aws_access_key] |
private_key | PEM private key material | [REDACTED:private_key] |
google_api_key | Google API keys | [REDACTED:google_api_key] |
slack_token | Slack tokens | [REDACTED:slack_token] |
github_token | GitHub tokens | [REDACTED:github_token] |
The related-but-different checks.body.detect_sensitive_data hook additionally
recognizes stripe_key, but blocks on the first hit and never redacts.
stripe_key cannot be named in a DLP block/redact list — it is detect-only.
Block vs redact: precedence
DLP finds all matches in the body, then applies:
- Block wins. Any match whose kind is in
blockblocks the message (findingbody.dlp_block:<kind>) before any redaction happens — a hard secret fails closed; no partially-scrubbed body ever leaves. - Redact. If nothing blocked, every match whose kind is in
redactis masked in place and the rewritten body is forwarded.
The recommended split: things whose presence is an incident (keys, private
keys, tokens) → block; things that are legitimate data you must not expose in
full (cards, SSNs, emails) → redact.
Whether a block actually blocks is governed by the policy mode — in
detect/shadow a block match is recorded as a would-block and the message
passes (see Modes & Fail Postures).
Redaction is not mode-gated. A redact match is a body mutation, not a
block, so it is applied whenever the body is inspected and a redact kind matches —
even under mode: detect or shadow. Only mode: off skips it, because off
performs no inspection at all. In other words: the block[] list follows the
policy mode; the redact[] list always rewrites the body.
How redaction works mechanically
Redaction rewrites the buffered body and sends the modified bytes back to Envoy through the ext_proc body-mutation channel:
- Shield returns a
BodyMutationcarrying the redacted body. - The
Content-Encodingheader is stripped — the body Shield inspected was already decompressed by the structural decode stage, so the mutated body is plaintext and must not be advertised as gzip. - Envoy recomputes
Content-Lengthfor the new body size; Shield does not set it. - Each mutated message is counted in the
body_mutations_totalmetric — watch it on the Overview dashboard to confirm a new redact policy is actually rewriting traffic.
Because a body-redacting policy must return its mutation on the body message,
Shield inspects the buffered body there even when trailers follow — a
BodyMutation cannot ride the trailers message. You get this behavior
automatically; it is called out only to explain why redacting policies behave
slightly differently from detect-only body policies in traces.
Anything that verifies the body downstream of Shield — body digests, HMAC
signatures, checksums — will see the redacted bytes, not the originals. If a
route combines DLP redaction with HMAC signing
(require_body_digest) or HTTP signatures on
the same direction, mind the pipeline-stage order (see
Built-in Checks & Pipeline Order).
Prerequisites checklist
inspect_response_body: true+max_response_body_bytes > 0fordirection: response; the request pair fordirection: request; both forboth. A DLP policy without body inspection on its direction inspects nothing.- The size cap must cover your real payloads — an over-limit body is blocked by the structural truncation guard, not passed unscanned (see Body Inspection & Limits).
- Findings carry the
dlpengine label infindings_total{engine="dlp"}and in Security Events, so DLP activity is separable from WAF/engine findings.
Example: block secrets on requests, redact PII on responses
apiVersion: sentinel.elchi.io/v1
kind: SecurityPolicy
metadata:
name: api-dlp
spec:
defaults:
mode: block
fail_mode: fail_close # a DLP error must not silently leak data
domains:
- hosts: ["dlp.example.com"]
routes:
- match:
path_prefix: "/"
policy:
mode: block
inspect_request_body: true
max_request_body_bytes: 1048576
inspect_response_body: true
max_response_body_bytes: 1048576
checks:
body:
dlp:
direction: both
# Hard secrets: a leak is an incident → block the message.
block: [private_key, aws_access_key, google_api_key, slack_token, github_token]
# PII: mask in place so the response stays usable.
# credit_card keeps the last 4; others become [REDACTED:<kind>].
redact: [credit_card, ssn, email, jwt]
If you only need the classic "scrub outbound PII" posture, scope it tighter:
direction: response, inspect_response_body: true, and no request-side
buffering — DLP adds latency and memory only where it runs, so keep it on the
routes that return sensitive data (see the sizing guidance in
Body Inspection & Limits).
Roll a block list out via mode: detect → shadow → block and watch
Security Events for false positives (the email
kind in particular can match legitimate payload fields). A redact list needs
no such ramp — redaction applies as soon as the policy is live (in any mode
except off), and body_mutations_total starts moving immediately. See
Modes & Fail Postures.