Look-alike (homoglyph) characters: Latin a vs Cyrillic а
The word looks right. The computer says it is a different word. One of the letters came from another alphabet.
Pairs you will actually meet
Each row below uses the real characters. In a font that distinguishes them poorly they look identical; the code points do not.
| Looks like | Latin | Cyrillic (and others) |
|---|---|---|
| a | U+0061 a | U+0430 а |
| e | U+0065 e | U+0435 е |
| o | U+006F o | U+043E о, Greek U+03BF ο |
| p | U+0070 p | U+0440 р |
| c | U+0063 c | U+0441 с |
| x | U+0078 x | U+0445 х |
| y | U+0079 y | U+0443 у |
| i / l / 1 | U+0069 i, U+006C l, U+0031 1 | also U+0131 ı (dotless i), U+0049 I |
| 0 / O | U+0030 0, U+004F O | U+041E О |
A whole brand name can be spelled with look-alikes: a string that prints as “apple” may contain no Latin letters at all. Browsers often show such domain names in punycode, starting with xn--, which is the giveaway.
Where they cause trouble
- Links and email. A look-alike domain can pass a quick glance. Read the name from the right and treat
xn--as “this is not the word it looks like”. The who owns this link page and Anvil /v1/url/inspect flaglookalike_letterswithout visiting the URL. Wirebook’s fake-message lesson practises the same skill. - Usernames and display names. Two accounts can look like the same person. Mixed scripts in a name are a warning sign, not proof of anything on their own (people have honest reasons to mix Latin and another alphabet).
- Source code and config. A variable or import that uses a look-alike letter will not refer to the identifier you think it does (related to, but not the same as, Trojan Source bidi tricks). Review tools and CI should flag mixed-script identifiers.
- Copied passwords and tokens. If someone typed a look-alike by hand, or a page displayed one, login fails in the same way as an invisible extra character. Retype from a trusted source rather than copying from a forwarded message.
How to tell letters apart
// JavaScript: list script-agnostic code points
[...text].map(c => "U+" + c.codePointAt(0).toString(16).toUpperCase().padStart(4, "0") + " " + c)
# Python
print([f"U+{ord(c):04X} {c}" for c in text])
Paste the suspicious string next to one you typed yourself in the text diff — the look-alike letters show as changes. Anvil /v1/text/inspect reports homoglyph and mixed_scripts findings on non-secret text. What is this? is a first pass if you are not sure what you pasted.
This is not the same problem as a hidden zero-width character or a non-breaking space. Those add marks you cannot see; a homoglyph substitutes a mark you can see.
Related
Who owns this link · Invisible zero-width characters · Strings that look the same but are not equal · Invisible characters in a password · Anvil text X-ray · Anvil link X-ray · Wirebook: fake messages
Questions
What is a homoglyph?
A character that looks like a different character but has another code point, such as Latin a (U+0061) and Cyrillic а (U+0430). They print the same in many fonts and compare as not equal.
How do look-alike letters appear in a web address?
Internationalised domain names can use letters from other alphabets. Browsers often display mixed-script names as punycode starting with xn--, which is a sign the name is not the simple word it resembles.
How can I check a string for homoglyphs?
List every code point (see the snippets above), paste it next to a trusted copy in the text diff, or send non-secret text to Anvil /v1/text/inspect, which flags homoglyph and mixed_scripts.
Is every mixed-script name a scam?
No. People write real names and words that mix Latin with another alphabet. Treat mixed scripts and xn-- domains as a reason to look more carefully, not as proof.
Are homoglyphs the same as invisible characters?
No. Invisible characters (zero-width spaces, bidi marks) add something you cannot see. Homoglyphs replace a letter with a look-alike you can see. Both break equality; they need different checks.
Published .