Invisible text just switched teams.
ASCII smuggling — the trick of hiding content inside Unicode characters that render as absolutely nothing to a human reader — spent its recent fame as an attack on AI systems. You hide instructions in a block of invisible codepoints, paste it somewhere a model will read it, and the model happily processes text that no person on the review chain can see. Now Microsoft says email spammers have picked it up to slip past filters, and its telemetry shows a sharp rise in phishing campaigns using it. Ars Technica covered the shift, and it spread fast across security feeds.
What makes this interesting to me isn’t the phishing angle. It’s the direction of travel. An AI-era evasion technique crossed over into ordinary email abuse, and the finding came out of Microsoft’s own prompt injection protection research for Defender for Office 365. The AI safety work found the spam trick. That says something about how these two problems are actually one problem wearing different hats.
Same bytes, two different readers
Every text pipeline I build has at least two consumers: a human and a machine. The human sees rendered glyphs in a browser or a mail client. The machine sees a byte sequence. ASCII smuggling lives in the gap between those two views. Characters with no visible form pass straight through rendering while remaining fully present in the string a parser, a filter, or a language model receives.
Prompt injection abused that gap in one direction — feeding hidden instructions to a model while a reviewer sees clean text. Spam filter evasion abuses it in the other — breaking up the visible signature of a suspicious phrase so pattern matching misses it, while the recipient’s eye reassembles the message perfectly. The mechanism is identical. Only the target changed.
Why builders should care even without an email product
If you’re wiring up bots, you are almost certainly moving untrusted text into a model, and probably out again into a log, a database, a webhook, or another agent. Hidden characters survive every one of those hops unless you deliberately remove them. A few places this bites:
- Retrieval pipelines. Anything you scrape or ingest can carry passenger text. It lands in your vector store, comes back on a semantic match, and enters the context window with no visible trace in your admin UI.
- Human review loops. Moderation queues and approval steps are worth very little if the reviewer’s screen shows a different string than the model consumed. You’ve built a control that inspects the wrong artifact.
- Agent-to-agent handoffs. One bot’s output becomes another bot’s input. Invisible content rides along and inherits whatever trust the second bot extends to the first.
- Your own filters. If you built keyword or regex guardrails, the spam story applies to you directly. Hidden separators inside a banned phrase defeat naive matching the same way they defeat mail filters.
What I actually do about it
Normalization at the boundary, not in the middle. Untrusted text gets cleaned the moment it enters my system, before storage, before embedding, before any model sees it. Concretely, the habits that have held up for me:
- Strip or reject codepoints with no visual representation rather than trying to interpret them. If a character can’t be seen, it has no business in user-supplied prose.
- Apply Unicode normalization consistently, and pick one form for the whole pipeline so comparisons behave predictably.
- Count what you strip and log it as a signal. A message that shed a pile of invisible characters on the way in is a message worth flagging, not just fixing quietly.
- Render raw text in review tooling with escapes visible, so a human reviewer sees what the parser sees. This one change turns an invisible attack into an obvious one.
- Test with adversarial fixtures. Put a smuggled payload in your test suite so a future refactor can’t silently drop the sanitizing step.
None of that is exotic. It’s input validation, applied to a class of characters most of us never thought about because they never showed up on screen.
The broader lesson
Techniques don’t stay in their lane. A method developed to manipulate models turned out to work fine against decades-old filtering infrastructure, and the crossover was spotted by people looking at the AI side of the house. That flow will keep going both ways. Old spam tricks will find new life against agents; new agent tricks will find new life against email, chat, ticketing systems, and anything else that pattern-matches text.
The practical takeaway for anyone building bots is unglamorous and cheap. Treat text as bytes, not as what you see rendered. Clean it at the door. Log the cleanup. Show reviewers the truth. Solid input handling isn’t a new idea, but the invisible layer of Unicode is a reminder that “text” is a bigger surface than it looks — which is exactly the point of hiding things there.
🕒 Published: