Abstract navy illustration of Unicode prompt injection: a row of slate bars seen through two lenses, one revealing a hidden gold thread between them

A few days ago a static analysis scan stopped one of my own commits. The finding was “bidirectional characters in source.” I had asked an AI coding tool to write a small function that strips a range of Unicode control characters from text, and I had written the range as escape sequences, the six-character \u202e kind. Somewhere between my request and the file on disk, the escapes were turned into the characters themselves. The code still worked exactly as intended. It just contained invisible characters whose whole purpose is to change the order in which text is displayed, sitting in a line of source code that a reviewer would read on a screen. It then happened a second time, in the first draft of this post, inside the very sentence you just read; a fact-checking pass caught it.

Nothing bad happened. But it sent me down a path I want to share, because the characters in that commit belong to a family that shows up in two places that are usually discussed separately: phishing and prompt injection. The tricks are the same. Only the victim changes.

Every string has two readers

When you look at text on a screen, you are reading a rendering. The bytes underneath are a sequence of code points, and the software that draws them makes decisions along the way: which characters are visible, which way a run of text flows, which glyph to use for a letter. Most of the time the rendering and the bytes say the same thing. Unicode contains a few hundred code points for which that is not true, and as far as I know, each was added for a legitimate reason.

That gap is the whole attack surface. A string has two readers: a person, who sees the rendering, and a machine, which processes the bytes. A mail filter is a machine reader. So is a compiler, and so is a large language model. An attacker who controls the string decides which reader to fool.

  • Fool the machine, inform the human. Phishing does this. The lure has to reach a person and persuade them, so it must read normally on screen while slipping past whatever filter sits in front of the inbox.
  • Fool the human, inform the machine. Prompt injection and the attack known as Trojan Source do this. The instruction has to reach the model or the compiler intact, while the person who might review the text sees something harmless, or nothing at all.

The toolkit

Four families of characters do most of the work.

Bidirectional controls. Unicode supports mixing left-to-right scripts such as English with right-to-left scripts such as Arabic and Hebrew. A set of invisible control characters (U+202A to U+202E and U+2066 to U+2069) tells the renderer how to order a run of text. The most abused is U+202E, RIGHT-TO-LEFT OVERRIDE, which forces everything after it to display in reverse. The bytes stay in their original order, so the machine reads one thing and the person sees another.

Zero-width and format characters. The zero-width space, joiner and non-joiner, the word joiner, the soft hyphen and the byte order mark have no visible form in most contexts. Put one inside a word and the word looks unchanged to a person, but it no longer matches the same word in a keyword list.

Tag characters and variation selectors. The Unicode Tags block (U+E0000 to U+E007F) mirrors printable ASCII, one invisible character per letter. It exists for language tagging and, today, for the flag emoji of England, Scotland and Wales. Anything else you find there deserves suspicion, because an entire sentence can ride along inside an innocent-looking string. Variation selectors do something similar: Paul Butler showed that 256 of them are enough to attach arbitrary bytes, one selector per byte, to any visible character, including an emoji [9].

Homoglyphs. The Cyrillic small letter a (U+0430) and the Latin “a” are different letters that look the same in most fonts. So do fullwidth letters, mathematical bold letters, and a long tail of lookalikes in Greek, Armenian and the Latin extension blocks. A word spelled with them reads normally and matches nothing.

In phishing: hiding from the filter

Phishing is still one of the most common ways attackers get in. The 2026 Verizon Data Breach Investigations Report, built from more than 31,000 incidents and more than 22,000 confirmed breaches, puts phishing at 16% of breaches as an initial access vector and finds the human element in 62% of breaches [1]. Any trick that gets a lure past the filter and in front of a person is worth money to an attacker, so these characters keep coming back.

Invisible characters inside keywords. In early 2019, Avanan disclosed an attack it called Z-WASP, in which zero-width spaces were inserted inside malicious links in email. Microsoft’s processing did not recognize the result as a link, so reputation checks and link protection never applied. Avanan reported seeing the attempt against more than 90 percent of its own Office 365 customers before Microsoft fixed it on January 9 [2].

This year it happened again with a different character set. Microsoft’s Defender for Office 365 researchers, while working on prompt injection protection, saw their hunting signature for “ASCII smuggling” spike on February 9, 2026. A high-volume campaign was inserting an invisible tag character inside finance words, so “funding” arrived as “fun”, an invisible character, then “ding”. The signature fired on roughly 21,000 messages on February 8, on more than 1.3 million on February 9, and on over 2.3 million on February 11. The high-volume phase lasted about three months, until a sharp drop after May 15 [3]. Microsoft’s title for the write-up says it plainly: the technique crossed over from AI prompt injection research into phishing evasion.

There is good news in that report too. Microsoft says more than 99% of those messages were caught by layers that did not depend on recognizing the tag characters at all [3]. Defense in depth worked. The invisible characters only defeated the controls that matched on words.

Reversed filenames. The right-to-left override has been used to disguise attachments for well over a decade. The ATT&CK knowledge base catalogs it as technique T1036.002, with examples such as a screensaver named March 25 [U+202E]xcod.scr that displays as March 25 rcs.docx, and it lists threat groups including BlackTech, BRONZE BUTLER and Ferocious Kitten among its users [4]. The trick still works because people judge a file by the name they see. In 2022 the email security firm Vade reported more than 200 right-to-left override attacks against Microsoft 365 users in a two-week window, dressing up phishing attachments as voicemail recordings [5].

Lookalike letters. Homoglyph domains and display names are the oldest member of the family and still the most familiar. A link to a domain that differs from the real one by a single Cyrillic letter looks exactly right in most mail clients.

In prompt injection: hiding from the human

Prompt injection is the top risk on the OWASP list for large language model applications, LLM01:2025 [6]. The indirect form is the one that matters for agents: the model reads external content, such as a web page, an email or a file, and that content contains text written to change the model’s behavior. OWASP notes explicitly that injected content does not need to be readable by a person to work.

How common is it? Measurement is just starting, and the honest answer is “growing, and still mostly crude.”

  • Google scanned Common Crawl, whose monthly snapshots each hold 2 to 3 billion pages, for known injection patterns. It found a mix of pranks, helpful guidance, SEO, attempts to deter AI agents and some malicious attempts at data exfiltration and destruction. The malicious category grew 32% in relative terms between November 2025 and February 2026, but Google characterized the sophistication as limited and saw little use of the advanced techniques published in 2025 research [7].
  • Palo Alto Networks Unit 42 documented in-the-wild cases, including what it describes as the first observed attempt to get an AI ad review system to approve a scam ad. Among the delivery methods in its telemetry, 37.8% were plain visible text, 19.8% hid the prompt in HTML attributes and 16.9% used CSS to suppress rendering. Its catalog of concealment techniques also includes zero-width characters, homoglyph substitution and the right-to-left override, without separate counts [8].
  • Last week I wrote about a census that found 30 sites, out of 217 million web responses, trying injection through HTTP response headers, a channel no human visitor ever sees [10].

So most injection on the web today is not Unicode trickery. It is plain text in a place the page’s human visitors are not looking. That is worth saying clearly, because it would be easy to overstate the invisible-character threat. But there are three reasons I think this family deserves attention now anyway.

First, the research is mature. In 2021, Nicholas Boucher, Ilia Shumailov, Ross Anderson and Nicolas Papernot showed that invisible characters, homoglyphs, reorderings and deletions degrade production language systems, and that “with three injections most models can be functionally broken” [11]. Boucher and Anderson’s Trojan Source paper showed the same bidi characters reordering source code so that a reviewer sees one program and the compiler builds another, assigned CVE-2021-42574, with a related homoglyph variant assigned CVE-2021-42694 [12].

Second, the techniques move. The phishing campaign Microsoft described took an idea from prompt injection research and used it at a volume of millions of messages a day [3]. I expect moving the other way to be even easier.

Third, and most important for anyone running agents, the human review step is the one these characters are designed to defeat. Many agent designs rely on a person glancing at what the agent read or is about to do. An instruction stored backwards inside a right-to-left override, or written in tag characters, survives that glance.

What a harness can do

By harness I mean everything around the model: the tools it calls, the hooks that inspect what goes in and out, and the approval steps in front of anything consequential. I wrote about several general controls last week [10]. Gating actions instead of words, keeping approvals in a channel the content cannot write to, and putting your detector in front of a hostile reviewer all apply here unchanged. These are the ideas specific to the two-reader problem.

1. Match on a folded copy, and on the raw one. Keyword and pattern detection should run on a copy of the text shaped like what a person would see. That means Unicode compatibility normalization (NFKC folds fullwidth and mathematical letters back to plain ones), removal of format characters and combining marks, a lookalike map for the common Cyrillic and Greek letters, and reversing right-to-left override runs into display order. One subtlety is worth a sentence of its own: an invisible character inside a word should vanish, but one standing in for a space should become a space, so it pays to match against both shapes. Microsoft’s first recommendation after the tag-character campaign was the same idea: normalize before you match [3].

2. Treat certain characters as signals, and spend your false-positive budget carefully. Some code points have almost no business appearing in ordinary English content. Tag characters outside the subdivision flag sequences Unicode currently recommends (today, England, Scotland and Wales), and bidi overrides, belong on that list. Others are entirely normal in the right context. Arabic and Hebrew text uses isolates and directional marks constantly, and word processors, mail clients and contact cards can leave directional marks and embeddings in ordinary Latin text, often around phone numbers. A detector that warns on every email signature will be switched off within a week. Judge bidi characters against the script of the surrounding document, ignore runs that wrap only a number, and never flag the lone directional marks.

3. Scan every door, not just the web. It is natural to put a scanner in front of web fetches and stop there. The phishing campaigns above arrived by email, and an agent that reads mail through a command-line tool, opens files from a cloned repository, extracts text from a PDF or reads a transcript is ingesting the same kind of untrusted text through doors that are not “fetches.” List every channel through which text written by someone else reaches the model, and make sure each one is inspected.

4. Show the code points when the name matters. Never judge an attachment or a file by its rendered name. When a harness reports a filename, a URL or a sender name to a person for a decision, it should render invisible and directional characters visibly, as [U+202E] for example. For filenames, compare the extension after the last dot of the stored name with the declared content type.

5. Warn on the human’s own input, do not erase it. People paste suspicious text into an assistant precisely to ask about it. If a scanner on the user’s prompt blocks outright, it destroys the thing the person wanted examined. For findings that rest on folding or invisible characters, an alert is the better default.

6. Sweep your own source. The Trojan Source family is not only something that arrives from outside. My own commit shows it can be introduced by your tools. Run a check for literal bidi and zero-width characters at commit time, and write ranges of these characters in code as escapes or as numeric code points built at run time, never as the characters themselves.

7. Test with the tricks, not with a polite example. A detector that has only seen plain English injection has been tested against nobody. Build test payloads from code point numbers (variation selectors, soft hyphens, combining marks, dotless i, fake flags, reversed runs) and keep the legitimate cases in the same suite: Arabic and Hebrew text, a real England flag, an Outlook signature. The bypasses are cheap to write, and an adversarial reviewer, human or model, will find the ones you did not think of.

Where this goes

The invisible-character family is old. It shows up in a 2009 Firefox vulnerability that let a right-to-left override disguise the extension of a downloaded file [13], the 2019 zero-width phishing attack, the 2021 Trojan Source paper and this year’s tag-character campaign. Every few years it comes back with a different subset of Unicode, and every time the defense is the same: decide which reader you are protecting, and make sure your software sees what that reader sees.

For phishing, the reader to protect is the filter, so normalize before you match. For prompt injection, the reader to protect is the human who is supposed to be supervising the agent. The machine already reads the bytes. The job of the harness is to make sure the person is never the only one who cannot.

References

  1. Verizon, 2026 Data Breach Investigations Report: Executive Summary, May 2026
  2. Swati Khandelwal, Hackers Using Zero-Width Spaces to Bypass MS Office 365 Protection, The Hacker News, January 10, 2019
  3. Noam Kochavi and Sarah Wolstencroft, Microsoft Security Research, ASCII smuggling crosses over from AI prompt injection to phishing evasion, Microsoft Security Blog, September 3, 2026
  4. ATT&CK, Masquerading: Right-to-Left Override, T1036.002
  5. Sarah Coble, Microsoft 365 Phishing Attack Makes Comeback, Infosecurity Magazine, February 8, 2022
  6. OWASP Gen AI Security Project, LLM01:2025 Prompt Injection
  7. Thomas Brunner, Yu-Han Liu and Moni Pande, AI threats in the wild: The current state of prompt injections on the web, Google, April 23, 2026
  8. Beliz Kaleli, Shehroze Farooqi, Oleksii Starov and Nabeel Mohamed, Fooling AI Agents: Web-Based Indirect Prompt Injection Observed in the Wild, Palo Alto Networks Unit 42, March 3, 2026
  9. Paul Butler, Smuggling arbitrary data through an emoji, February 8, 2025
  10. Kenneth G. Hartman, The Header Your Agent Reads and You Never See, October 16, 2026
  11. Nicholas Boucher, Ilia Shumailov, Ross Anderson and Nicolas Papernot, Bad Characters: Imperceptible NLP Attacks, IEEE Symposium on Security and Privacy, 2022
  12. Nicholas Boucher and Ross Anderson, Trojan Source: Invisible Vulnerabilities, USENIX Security Symposium, 2023
  13. NIST National Vulnerability Database, CVE-2009-3376, October 29, 2009