DLP Rules That Catch Real Data Leaks Without Breaking Normal Work
For developers and platform teams designing data loss prevention that people won’t immediately route around. This guide shows how to build high-signal DLP rules around context, destinations, and response actions, with concrete examples you can adapt in mail, HTTP, logs, and CI/CD flows.
TL;DR — Most bad DLP deployments fail because they match strings instead of risky actions in context. The practical fix is to combine content detection with destination, actor, channel, and volume checks, then start in audit/quarantine mode and only hard-block the narrow cases you can prove are true leaks. Reading time: ~7 min
What it is and where it sits
DLP rules that actually work are not just regexes for credit cards or API keys. They are policy decisions applied at data egress points: email gateways, outbound HTTP proxies, SaaS upload controls, CI logs, chat/webhook relays, object storage sharing, and endpoint copy/export actions. The job is to catch data leaving trusted boundaries in the wrong way, not to classify every sensitive byte in the company.
In a typical stack, DLP sits between systems that produce sensitive data and systems that can send it somewhere else. It often replaces ad hoc grep-based blocking in CI, one-off mail filters, or manual reviews of suspicious exports. It does not replace application authorization, database row-level security, or encryption; it sits after those controls, where data is about to leave.
A useful mental model:
- App/database/classifier says what the data is.
- Identity system says who is acting.
- DLP policy engine decides whether this transfer is allowed.
- Enforcement point blocks, quarantines, redacts, or just logs.
View as text
[User/App] ---- 1. export/report/upload ----> [Egress Point]
|
| 2. content + context
v
[DLP Engine]
|
3a. allow ------+------ 3b. quarantine/block
|
v
[Destination/System]Where it commonly lives in request/data flow:
- Outbound email: after SMTP submission, before relay to the internet.
- Web uploads/API calls: forward proxy, secure web gateway, service mesh egress, or app middleware.
- CI/CD: log processor, artifact publish step, pre-merge secret scanner.
- Object storage: bucket policy + event-driven scanner before public share or cross-account replication.
- Internal apps: export/download endpoint, not every read query.
If you put DLP only at rest, you will find sensitive data but miss the moment it leaks. If you put it only on endpoints, you will miss server-to-server exfiltration. The architecture choice is really about where you can still stop or quarantine the transfer with acceptable latency.
How it actually works
The mechanism that catches real leaks is usually a pipeline:
- Normalize the payload.
- Extract signals from content.
- Add context from the transaction.
- Score or match rules.
- Choose an action by confidence and business impact.
- Log enough evidence for humans to review.
One realistic end-to-end example
Scenario: an engineer exports a CSV of customer records from an internal admin app and emails it to a personal Gmail address to work on it locally.
Step by step:
- The admin app generates
customers-2026-10-30.csvand sends it as an attachment through your outbound SMTP relay. - The mail gateway unpacks MIME parts, decompresses archives if present, and normalizes text from CSV/XLSX/PDF where possible.
- The DLP engine runs detectors:
- 1,842 email addresses found.
- 1,842 customer IDs matching your internal format like
CUST-[0-9]{8}. - 0 payment card hits after Luhn validation.
- File size 14 MB.
- It adds context:
- Sender is in Engineering, not Support or Finance.
- Recipient domain is
gmail.com, external and not on the approved vendor allowlist. - Channel is outbound email.
- Volume exceeds the “bulk customer export” threshold.
- The rule matches:
customer_data AND external_personal_email AND bulk_export. - Action is
quarantine, notreject, because false positives on attachments are expensive and the security team wants reviewable evidence. - The sender gets a deterministic SMTP response or post-submit notice. The SOC gets an event with rule name, sender, recipient, counts, and a secure preview hash, not the full attachment copied into random logs.
A realistic SMTP rejection/quarantine shape looks like this:
$ swaks --server smtp-relay.internal --to me@gmail.com --from dev@corp.example --attach @customers-2026-10-30.csv
=== Trying smtp-relay.internal:25...
=== Connected to smtp-relay.internal.
<- 220 smtp-relay.internal ESMTP
-> EHLO laptop.local
<- 250-smtp-relay.internal
<- 250-PIPELINING
<- 250-8BITMIME
<- 250 STARTTLS
-> MAIL FROM:<dev@corp.example>
<- 250 2.1.0 Ok
-> RCPT TO:<me@gmail.com>
<- 250 2.1.5 Ok
-> DATA
<- 354 End data with <CR><LF>.<CR><LF>
-> ...message body...
-> .
<- 250 2.7.0 queued as 8f2b1c91
=== Connection closed with remote host.
If the gateway quarantines after accept, the sender sees success but gets a separate notice. If it rejects inline, you’ll instead see something like:
<- 554 5.7.1 Message blocked by DLP policy "bulk_customer_data_to_external_mail"; event_id=dlp-7b9f3a
The important part is not the regex. It is the conjunction of content and context. A CSV with 1,842 emails sent to an internal analytics mailbox may be normal. The same CSV to a personal mailbox is a leak. Conversely, a single fake test card number in a Jira ticket should not block anything.
The rule shape that usually works
High-signal DLP rules tend to look like this:
- Content detector: secret, PII, source code, contract, schema dump, customer export.
- Context: external destination, unmanaged device, personal domain, public link, unusual actor, unusual time, unusual volume.
- Exceptions: approved vendors, ticketing systems, encrypted channels you explicitly allow, service accounts with documented purpose.
- Action ladder: log -> warn -> quarantine -> block.
That action ladder is what keeps normal work moving while you tune. Start with evidence collection, not heroics.
When to use it (and when not to)
Use DLP when the problem is data leaving through legitimate channels, not attackers smashing through auth. If your main risk is “engineers accidentally send production data to the wrong place,” DLP is a fit. If your main risk is “the app lets anyone query everyone’s records,” fix authorization first.
| Scenario | Recommendation |
|---|---|
| Outbound email regularly carries reports, exports, invoices | Use DLP at the mail relay with quarantine-first rules |
| Developers paste secrets into CI logs, tickets, or chat | Use secret scanning at source plus DLP on log/chat/webhook egress |
| Customer data is exported from admin tools | Put DLP on the export endpoint and on outbound channels |
| Mostly internal-only systems with no external egress except VPN | You probably don’t need full DLP; tighten egress and audit exports |
| Small team, low regulated data, no bulk exports | Start with secret scanning and storage-sharing controls, not broad DLP |
| Main issue is overbroad DB access or broken tenancy | Fix authz and data partitioning before adding DLP |
You probably don’t need this if:
- Your only “DLP” goal is catching committed API keys in Git; use pre-commit and CI secret scanners first.
- You cannot identify enforcement points. A policy engine with nowhere to block is just expensive logging.
- You have no review process. Quarantine without owners becomes silent work stoppage.
Trade-offs
Every DLP benefit comes with a cost. Plan for both.
- Fewer real leaks caught at egress
- Cost: latency on uploads/mail, plus operational complexity for parsing file formats and archives.
- Better signal by combining content with context
- Cost: more integration work with identity, device posture, destination allowlists, and business metadata.
- Lower false positives through quarantine-first rollout
- Cost: humans must review quarantined items quickly or users will route around the control.
- Coverage across many channels
- Cost: policy drift. Email, HTTP, object storage, and endpoint controls rarely expose identical signals.
- Better audit trail for privacy/compliance
- Cost: the audit trail itself can become sensitive. Don’t dump full payloads into SIEM indexes.
- Vendor policy engines can accelerate deployment
- Cost: lock-in around classifiers, incident workflows, and proprietary rule syntax.
The hardest trade-off is false positives versus false negatives. Teams usually over-index on “catch everything” and end up blocking normal work. In practice, a narrower rule that reliably catches bulk exports to personal destinations is worth more than a giant pattern pack that fires on test fixtures all day.
In practice
Example 1: YARA rule for high-entropy secrets in CI artifacts/log bundles
This is useful when you already collect build artifacts or logs and want a deterministic scanner in a pipeline or quarantine job.
rule Possible_AWS_Credential_Leak
{
meta:
description = "Detect likely AWS access key plus nearby secret"
severity = "high"
strings:
$akid = /AKIA[0-9A-Z]{16}/
$secret_label = /aws(.{0,20})?(secret|access).{0,20}(key|token)/ nocase
$secret_value = /[A-Za-z0-9\/+=]{40}/
condition:
$akid and $secret_label and $secret_value
}
Run it against a build output directory:
$ yara -r rules.yar ./artifacts
./artifacts/job-1842/terraform.log(3921): Possible_AWS_Credential_Leak
What it does: it requires multiple nearby-ish indicators instead of flagging every AKIA... string in docs or tests. Gotcha: YARA alone has weak business context; pair the hit with path rules like artifacts/**, not docs/**, or you will annoy everyone.
Example 2: Postfix header/body checks to hold likely leaks to external domains
If you run Postfix as an outbound relay, you can route suspicious mail to HOLD for review. This is blunt but effective for a first pass.
⚠️
postmapandpostfix reloadchange live mail handling. A bad regex can delay or hold legitimate mail for the whole organization. Test on a staging relay or a restricted sender group first.
/etc/postfix/main.cf:
smtp_tls_security_level = may
header_checks = pcre:/etc/postfix/header_checks.pcre
body_checks = pcre:/etc/postfix/body_checks.pcre
/etc/postfix/header_checks.pcre:
/^To:.*@(gmail\.com|outlook\.com|yahoo\.com)/ HOLD possible_external_personal_recipient
/^Cc:.*@(gmail\.com|outlook\.com|yahoo\.com)/ HOLD possible_external_personal_recipient
/etc/postfix/body_checks.pcre:
/AKIA[0-9A-Z]{16}/ HOLD possible_aws_key
/(ssn|social security number|customer export|confidential)/i HOLD possible_sensitive_content
Build maps and reload:
$ sudo postmap /etc/postfix/header_checks.pcre
postmap: warning: /etc/postfix/header_checks.pcre, line 1: record is in "key: value" format; is this an alias file?
$ sudo postfix check
$ sudo postfix reload
That postmap warning is expected for PCRE tables on some systems; postfix check is the one to trust here. What it does: any matching message is held in the queue for review. Gotcha: body/header checks do not understand attachment semantics well; for real attachment inspection you need a milter/content filter, not just regex on message text.
Inspect held mail:
$ mailq | grep HOLD -B2 -A3
-- 14 Kbytes in 1 Request.
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
8F2B1C91* 14336 Fri Oct 30 10:42:11 dev@corp.example
me@gmail.com
(HOLD: possible_external_personal_recipient)
Example 3: Export-endpoint guard in application code
Catching leaks before the file exists is often cheaper than scanning every outbound channel.
app.post('/admin/exports/customers', async (req, res) => {
const actor = req.user;
const destination = req.body.destination; // "download", "s3", "email"
const rowCount = await estimateCustomerExportRows(req.body.filter);
const externalEmail = destination === 'email' && /@(gmail\.com|outlook\.com|yahoo\.com)$/i.test(req.body.email);
const highVolume = rowCount > 1000;
const nonSupportUser = actor.department !== 'Support';
if (externalEmail && highVolume && nonSupportUser) {
req.log.warn({ actor: actor.id, rowCount, email: req.body.email }, 'DLP export blocked');
return res.status(403).json({
error: 'dlp_policy_block',
message: 'Bulk customer exports cannot be sent to personal email. Use the approved secure share workflow.'
});
}
return queueExport(req, res);
});
What it does: blocks the exact risky behavior instead of trying to infer it later from SMTP traffic. Gotcha: don’t hardcode consumer domains forever; move them to config and add approved external recipients for vendors under contract.
Further reading
- NIST SP 800-53 Rev. 5, "SI" and "SC" control families
- NIST SP 800-122, "Guide to Protecting the Confidentiality of PII"
- Postfix "header_checks" and "body_checks" documentation
- YARA "Writing YARA Rules" documentation
- The "Data Loss Prevention" sections of your cloud provider and mail gateway official docs
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI