Skip to main content

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

OptionTypeDefaultDescription
directionstringresponseWhere DLP runs: response | request | both.
blockstring[]emptyDLP kinds that cause a block.
redactstring[]emptyDLP kinds masked in place.

At least one of block / redact is required.

DLP defaults to the response direction

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

KindWhat it matchesRedaction behavior
credit_cardPayment card numbers (Luhn-validated)Keeps the last 4 digits
ssnUS Social Security numbers[REDACTED:ssn]
emailEmail addresses[REDACTED:email]
jwtJSON Web Tokens[REDACTED:jwt]
aws_access_keyAWS access key IDs[REDACTED:aws_access_key]
private_keyPEM private key material[REDACTED:private_key]
google_api_keyGoogle API keys[REDACTED:google_api_key]
slack_tokenSlack tokens[REDACTED:slack_token]
github_tokenGitHub tokens[REDACTED:github_token]
note

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:

  1. Block wins. Any match whose kind is in block blocks the message (finding body.dlp_block:<kind>) before any redaction happens — a hard secret fails closed; no partially-scrubbed body ever leaves.
  2. Redact. If nothing blocked, every match whose kind is in redact is 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 BodyMutation carrying the redacted body.
  • The Content-Encoding header 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-Length for the new body size; Shield does not set it.
  • Each mutated message is counted in the body_mutations_total metric — 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.

Redaction changes the bytes downstream sees

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 > 0 for direction: response; the request pair for direction: request; both for both. 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 dlp engine label in findings_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).

Rollout

Roll a block list out via mode: detectshadowblock 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.