Confusable Characters: Why Look-Alike Letters Are a Security Risk
The Unicode standard assigns code points to characters from hundreds of scripts. Many of these characters look visually identical—or nearly so—to characters from other scripts, even though they are completely different code points. These are called confusable characters (or homoglyphs), and they're at the root of a class of attacks known as homograph attacks.
How Homograph Attacks Work
The classic example is the IDN homograph attack on domain names. Because internationalised domain names (IDNs) allow non-ASCII characters, an attacker can register a domain that looks identical to a legitimate one. The Cyrillic а (U+0430) and Latin a (U+0061) are indistinguishable in most fonts. A link showing "аpple.com" could be pointing to a Cyrillic-a domain—not Apple's real website.
Beyond domains, confusables are used to bypass username uniqueness checks, poison auto-complete suggestions, evade keyword filters, and smuggle malicious text through moderation systems.
Unicode's Confusables Data
The Unicode Consortium publishes a Security Mechanisms specification (UTS #39) that includes a machine-readable list of confusable pairs. This data is used by browsers, registrars, and security tools to detect suspicious mixed-script strings. CharLookup includes this data—on any character page you can see which characters it may be confused with.
Examples of Common Confusables
- Latin A (U+0041) ↔ Greek Alpha (U+0391) ↔ Cyrillic А (U+0410)
- Latin o (U+006F) ↔ Greek omicron (U+03BF) ↔ Cyrillic о (U+043E)
- Latin 1 (U+0031) ↔ Latin l (U+006C) ↔ Latin I (U+0049)
Use the character comparison tool to compare two characters side by side and see their Unicode properties, encodings, and script assignments. It's the quickest way to confirm whether two characters that look the same really are the same.
Defences
- Apply Unicode normalisation before comparing strings (NFKC collapses many compatibility variants).
- Restrict identifier scripts: if your application only needs Latin characters in usernames, reject code points from other scripts.
- Use skeleton matching from UTS #39: map each identifier to a canonical skeleton string and reject new identifiers whose skeleton collides with an existing one.
- Flag mixed-script strings: a username containing both Latin and Cyrillic characters is suspicious and worth extra scrutiny.