Data exfiltration

The break-in is loud; the theft is quiet — data leaving slowly looks almost like normal traffic.

The idea

Once someone is inside — a real attacker, a malicious insider, or a compromised service account — the dangerous step isn't getting in, it's copying sensitive data out. They rarely smash a hole in the wall. They lean on channels you already allow: HTTPS to a cloud bucket, DNS lookups, a legitimate SaaS API.

Data exfiltration is the unauthorized transfer of data off your systems, often slow, encrypted, and blended into ordinary egress so it stays under your alerts. The perimeter firewall sees nothing wrong — the data is leaving through a door marked "open". Detection lives in egress baselines and data-loss monitoring, not in the front gate.

Press play to watch outbound traffic on the egress dashboard — normal at first, then a quiet bulk read.

How it works

The failure mode: nobody watches what leaves. Reads are over-permissioned, outbound traffic has no baseline, and the destination list is open, so a service account can stream a customer table to an unfamiliar bucket and look like ordinary HTTPS.

The fix is to treat egress like a tap you can measure and close. Keep a rolling per-destination baseline, allow-list where data is allowed to go, and alert or block when a destination is new or volume jumps to many times normal. Below, the check both compares to the baseline and gates on the allow-list.

def egress_guard(flow, baselines, allow_list):
    # flow = one outbound transfer: destination + bytes, this window.
    dest = flow.destination
    mb = flow.bytes / 1_000_000
    base = baselines.rolling_mb(dest)        # normal MB for this dest

    # 1. Destination not on the allow-list — block, don't just log.
    if dest not in allow_list:
        block(flow)
        alert("egress.new_destination", dest, mb)
        return

    # 2. Allowed dest, but volume far above its baseline — bulk export.
    if base == 0 or mb > 5 * base:           # 5x the rolling baseline
        throttle(flow)
        alert("egress.volume_spike", dest, mb, ratio=mb / max(base, 1))

    # 3. Least privilege caps the blast radius even if this slips.
    enforce_read_quota(flow.principal)       # one creds != whole table

Signals

SignalWhat exfiltration looks like
Outbound volumeEgress far above the normal baseline for that window
DestinationLarge transfers to new or rarely-seen external endpoints
TimingData leaving at odd hours, off the usual batch schedule
Access breadthOne role reading far more records than it normally touches
Shape of trafficCompressed / encrypted blobs to cloud storage, or DNS tunneling

Watch out for

Worked example

Your normal nightly egress to known backups is about 200 MB. A compromised service account starts copying a customer table to an unfamiliar bucket at roughly 2 GB/hour. Over six hours that's about 12 GB — millions of rows — every one of them riding ordinary HTTPS, so the perimeter firewall stays green the whole time.

An egress baseline that alerts at 5x normal would have fired in the first hour, when 2 GB blew past the 200 MB line to an endpoint nobody had allow-listed. The slow-and-low variant — paced to a few hundred MB/hour to a known-looking domain — slides under a fixed threshold, which is why destination allow-listing and cumulative-volume tracking matter alongside rate. And a role-based read cap is the cleanest stop of all: if that service account could never SELECT the whole table, the bulk export never assembles in the first place.

Check yourself

Your perimeter firewall shows nothing unusual, but customer data is quietly leaking. Where do you look first?

Coach note: the theft leaves through doors you already allow, so the signal is in what goes out and where it's headed — compared against what "normal" looks like.