Three dots do not make a JWT

A JSON Web Token is three base64url segments joined by dots. Writing that as a pattern takes about thirty seconds, and the result is unusable.

The naive rule and what it hits

"Three chunks of base64url separated by dots" describes a JWT. It also describes a great deal of text that is not a JWT:

Base64url uses letters, digits, hyphen and underscore. That is most of the characters people put in identifiers, so "looks like base64" is barely a constraint at all. A rule this loose in a redaction tool produces a page full of highlights, which trains you to stop looking at them.

The constraint that fixes it

A JWT's first two segments are not arbitrary base64. They are base64-encoded JSON objects, and a JSON object starts with {.

Base64 encodes three bytes into four characters, deterministically. Any text starting with {" encodes to something starting with eyJ, every single time. It is not a convention or a common case; it falls out of the arithmetic.

So the rule requires the first two segments to begin with eyJ:

\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b

The third segment is the signature, which is raw bytes rather than JSON, so it stays unconstrained.

The measurement

Running the five cases above through the live rule:

InputResult
Genuine JWTcaught
Three base64 chunks, not a JWTno match
v1.2.3-beta.4567890abcdefno match
somefile.tarball.gz1234567890abcno match
Sentry DSN fragmentno match

One constraint, borrowed from the format's own specification, removes the entire false-positive class while keeping every real token.

The general lesson

This is the difference between a pattern that describes a shape and a pattern that describes a format. Three-dots-and-base64 is a shape, and shapes are shared by things that have nothing to do with each other. "Base64 of a JSON object" is a format, and formats have constraints you can lean on.

Whenever you write a detection rule, look for the part of the spec that forces a fixed value: a magic byte, a version prefix, a checksum, a mandatory field. That is where the precision comes from. Character classes and length bounds alone will not get you there.

What this rule still cannot do

It finds JWTs. It does not tell you whether the token is expired, whether it is yours, or whether it matters. A JWT in a public example in documentation gets redacted exactly like the live session token in your curl command, because they are identical in shape.

That is the right behaviour for a redaction tool: err toward hiding. But it means a highlighted JWT is not automatically a problem, and it is worth a glance before you assume the worst.