Why NFC Normalization Still Allows Look-Alike Usernames

Your signup form runs NFC, yet “admin” with a Latin a and “аdmin” with a Cyrillic а both get through. What does it take to catch look-alike usernames?

Why NFC Normalization Still Allows Look-Alike Usernames
TLDR
  • NFC makes canonically equivalent representations match; it does not merge every look-alike letter.
  • A confusable skeleton is an internal comparison value, not a replacement username.
  • Choose a registration policy separately from account equality, and account for changing Unicode data.

Equivalent text and similar appearance are different tests

Latin a (U+0061) and Cyrillic а (U+0430) remain different after NFC normalization. Their visual resemblance does not make them canonically equivalent. That is why a normalized username system can still admit look-alike names.

The Unicode normalization FAQ describes a different job: making equivalent encodings consistent. A precomposed é and an e followed by a combining acute accent become equal under NFC.

PairAfter NFCRelevant check
Precomposed é / e plus combining accentEqualCanonical equivalence
Latin a / Cyrillic аDifferentVisual confusability

NFKC removes compatibility distinctions too, which can discard information. Choosing it does not settle the separate question of which visually confusing names your service should admit.

A matching skeleton does not identify the same account

UTS #39 defines skeletons for comparing confusability. They are internal comparison values, unsuitable as display text or as a normalization of identifiers.

If a proposed name matches an existing name's skeleton, a service can reject the registration, request another name or send it for review. Those are registration-policy choices. Silently converting the new name into the existing account's login identifier would conflate resemblance with identity.

Compare names and check individual identifiers separately

ICU4J SpoofChecker exposes areConfusable for comparing two strings and failsChecks for checking one string against configured restrictions. These answer different questions: “Does this resemble that name?” and “Does this identifier fail our character policy?”

For a collection of protected names, ICU also illustrates building a set of getSkeleton values. The documentation warns that these values can change between ICU releases and recommends computing them at runtime instead of relying on a permanent, hard-to-update database of skeletons.

Preserve legitimate multilingual names

A mixed-script name is not automatically an impersonation attempt. UTS #39's Highly Restrictive category includes Latin, Han and Hangul together, subject to identifier syntax and the applicable character profile.

RFC 8265 distinguishes case-mapped and case-preserved username profiles. It also identifies both false acceptance and false rejection as security concerns in identifier comparison. A call to NFC alone is not a complete implementation of a username profile.

Define which names are valid and which representations identify the same account. Then apply confusable detection to registration decisions. Keeping those jobs separate lets the service detect misleading names without turning visual similarity into an authentication rule.