Why Your Profanity Filter Misses 1337: Catch the Mixed-Token Bypass

Why Your Profanity Filter Misses 1337 article cover

Your filter blocks a banned word, but does it still recognize the same word after 3 letters become digits? We often treat leetspeak as old internet slang. Yet strings like h3ll0, p@ssw0rd, and b4dw0rd are still a useful test for broken moderation, username, and log-triage rules. The failure is not exotic cryptography; it is a normalization gap that attackers can exploit before your exact-match rule ever runs.

The bypass happens before matching

Most text rules assume that the input and the blocklist share the same alphabet. A literal check for a banned term therefore misses a visually similar version in which a becomes 4, e becomes 3, or o becomes 0. Case folding does not fix that mismatch. Unicode normalization does not reverse it either.

The practical repair is to add a normalization stage before the policy stage. Decode suspicious substitutions, keep the original input for evidence, and run the policy against both versions. The Leetspeak Decoder & Detector is useful here because it separates two jobs that are often blurred together: producing a decoded candidate and estimating whether the input plausibly contains leetspeak.

That separation matters. A permissive decoder can transform ordinary characters, while a conservative detector can miss novel glyphs. Treating either result as ground truth creates a new failure while trying to fix the first one.

Mixed tokens are the useful signal

Leetspeak normalization workflow: keep raw input, decode a candidate, match both views
A safer normalization flow keeps the original evidence intact.

The detector does not label all numbers as leetspeak. It tokenizes runs made from ASCII letters, digits, @, underscore, and hyphen. A token scores above zero only when it contains at least one letter and at least one recognized single-character leet symbol. Therefore 8675309 is treated as a number, while h3ll0 is treated as a mixed token.

For each mixed token, the local score is the number of leet-shaped symbols divided by the token length. The overall 0–100 score combines two signals: 75 percent comes from the share of leet symbols across all token characters, and 25 percent comes from the share of tokens that are mixed. This design rewards a message containing several suspicious words more than a paragraph containing one accidental digit.

For example, according to the source implementation, h3ll0 w0rld forms two mixed tokens with 3 digit substitutions across 10 characters. The weighted formula produces a score of 48, not an automatic certainty. That is a better mental model than “digits equal abuse”: the score ranks evidence, while your downstream policy decides what the evidence means.

A threshold is a policy knob, not a truth meter

The tool defaults to a 40 percent threshold and allows values from 1 through 99. A score at or above the selected threshold receives a “Likely leetspeak” verdict. A lower score can still be labeled “Possibly leetspeak” when it reaches either 15 or half the configured threshold, whichever is higher.

This is where product context should replace intuition. A public chat filter may choose a lower threshold and send borderline messages to a second classifier. An account-creation flow should avoid rejecting a username solely because it mixes letters and digits. A security log pipeline can preserve each score, then sort high-scoring events for investigation instead of deleting them.

In our case, the threshold should change the next action, not erase the input. Record the raw string, normalized candidate, score, token breakdown, matched substitutions, and policy version. Those fields show why a rule fired and make false positives reproducible.

Decode twice, decide once

Leetspeak mixed-token score signals and policy threshold
Mixed-token signals separate likely leetspeak from ordinary numbers.

The online analyzer offers three modes: decode plus detect, detect only, and decode only. Its decoder greedily tests longer glyphs before shorter ones, then applies a fixed priority when several glyphs can represent the same letter. That creates a deterministic candidate, which is valuable for testing.

But deterministic does not mean linguistically correct. The digit 1 can represent i or l; 7 can represent several letters in extended alphabets. The decoder is intentionally permissive and substitutes whenever a mapping matches. A pure numeric ID can therefore receive a detection score of zero while its decoded display still changes digits into letters.

The safe workflow is dual-track. Keep the original string immutable. Generate the decoded candidate as a second field. Run matching against both, but show moderators and auditors which substitutions were applied. This preserves evidence and prevents normalization from silently rewriting customer data.

Test the detector’s blind spots

The substitution table includes multi-character shapes and symbols beyond the detector’s token alphabet. The detector, however, only forms tokens from letters, digits, @, underscore, and hyphen. A construction using $, #, !, or a dense multi-character glyph may be decoded but can receive less detection weight because those characters split tokens or fall outside the single-character scan.

That is not a reason to discard the score. It is a reason to test it. Build a small adversarial benchmark containing clean prose, phone numbers, product IDs, mixed alphanumeric usernames, classic leet words, punctuation-heavy variants, and multilingual text. Measure false positives and false negatives at several thresholds. The tool accepts up to 20,000 characters, so the same test can cover individual values and multi-line log extracts.

For instance, compare support123, supp0rt, pa$$word, and a numeric order ID. Then inspect the per-token table rather than relying only on the overall badge. The detector page highlights in-word substitutions and returns the score, verdict, threshold, token counts, and decoded candidate as metadata, which makes regression cases easier to document.

Put normalization in a layered defense

Leetspeak evidence fields for a reversible moderation pipeline
Versioned evidence makes moderation decisions reproducible.

Leetspeak normalization should not replace rate limits, contextual moderation, password guidance, or a stronger content classifier. It should create another view of the same event. Exact rules are fast and explainable; normalized rules catch common disguises; contextual models handle intent. Each layer can fail differently, so the combination reduces blind spots without pretending that one score proves abuse.

Design the pipeline so policy changes remain reversible. Store the original, normalized form, detector version, and decision separately. If a mapping or threshold changes, you can replay historical cases and measure the effect before shipping it. That feedback loop can improve the rule instead of forcing support teams to guess why yesterday’s acceptable username fails today.

Ultimately, the point is not to prove that each 3 is an e; it is to stop one alphabet swap from bypassing an otherwise sound rule. Try a few real policy cases with the detector, then compare the normalized and original outcomes. The next question is whether your system can explain each difference without destroying evidence. If it can, explore the broader Elysia Tools collection and keep building tests around the transformations your users can see but your matcher cannot.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *