Zero-width steganography: how text hides inside text
· 10 min read
Several schemes hide arbitrary data in ordinary-looking text by encoding bits as invisible characters. Each leaves a recognizable fingerprint: StegCloak uses the invisible math operators U+2061–U+2064 in one contiguous run, base-N schemes use fixed-width digits from a small alphabet, and variation selectors offer 240 symbols per position. Whitespace schemes hide bits in trailing spaces and tabs, where no code point is unusual and only the pattern gives it away.
StegCloak
StegCloak encodes two bits per character over a six-symbol alphabet: U+200C, U+200D and the four invisible math operators U+2061 through U+2064. The payload is compressed, optionally encrypted with AES-256-CTR, and inserted as a single unbroken run immediately before a randomly chosen word in the first half of the cover text.
The fingerprint is the alphabet. U+2061–U+2064 exist for MathML and appear almost nowhere else, so their presence in prose is close to conclusive. The first character of the run is a header flag identifying whether the payload is encrypted and whether it carries an HMAC, which lets a detector report the mode rather than just the presence.
Base-N zero-width encodings
A more general family treats a set of N invisible characters as digits in base N and encodes each character of the payload as a fixed-width group. With the common four-character alphabet — U+200C, U+200D, U+202C, U+FEFF — each UTF-16 unit takes eight carrier characters.
Unlike StegCloak these are often scattered through the cover text rather than kept contiguous, which makes run-length detection less useful. They are, however, brute-forceable: try the plausible alphabets and orderings, decode, and check whether the result reads as text.
Variation selectors
Unicode defines 256 variation selectors — 16 in U+FE00–U+FE0F and 240 more in U+E0100–U+E01EF. Each can follow a base character without producing a glyph of its own, which makes the supplement a wide channel: one byte per selector, appended to a single visible character.
The legitimate uses are narrow — emoji presentation and CJK glyph variants — so a selector following a Latin letter has no defensible explanation.
Whitespace steganography
Not every scheme uses exotic code points. Tools in the SNOW lineage encode bits in trailing spaces and tabs at the end of each line, where every character involved is entirely ordinary.
No code-point blocklist can catch this. Detection has to look at the pattern: how many lines carry trailing whitespace, how long those runs are, and whether tabs appear in them. Editors leave the occasional stray space; they do not produce multi-character space-and-tab sequences on line after line.
A note on false positives
Detection that brute-forces decodings will happily produce a plausible-looking "hidden message" from characters that were never a payload. Persian prose is full of legitimate zero-width non-joiners; a BOM-prefixed CSV starts with U+FEFF; MathML uses the invisible operators for their intended purpose.
The guard is to be strict about what counts as a recovered message — no lone surrogates, no unassigned-heavy output, and a result that actually reads like words. Telling someone their text contains a secret when it does not is a worse failure than missing a real one.
Frequently asked
- Can encrypted steganography be detected without the password?
- Yes. Detection and decryption are different problems. The carrier characters and their structure are visible to anyone inspecting the code points, even when the payload itself stays unreadable without the key.
- Does copying text through a chat app remove hidden payloads?
- Sometimes, unpredictably. Some platforms strip certain zero-width characters, which is precisely why some schemes deliberately choose carriers that survive those filters. Never rely on an intermediary having cleaned the text for you.
Keep reading
- Invisible Unicode characters: a complete reference
Every category of invisible, zero-width and blank-rendering Unicode character, what each one is legitimately for, and when its presence is a signal that something is hidden in your text.
- Do AI watermarks use invisible characters? No — and here is the evidence
SynthID-Text and Anthropic's Claude watermark are statistical, living in the model's word choices rather than in any added character. Stripping invisible characters has no effect on them whatsoever.
- Invisible prompt injection with Unicode Tags
How the U+E0000 block hides instructions that people cannot see but language models read, why it works, and how to strip it before untrusted text reaches a model.