{
 "slug": "homoglyphs-unicode-confusables-wire-payees-cheapest-attack-financial",
 "topic_id": "TOPIC-011",
 "cluster": "B2B Wire, AP & Treasury Payment Release",
 "tier": "Tier B",
 "title": "Homoglyphs and Unicode confusables in payee names: the cheapest attack on approvals",
 "summary": "Attackers register payee names using lookalike characters. The database stores distinct strings; the approval screen renders them identically. The approver compares two things that look the same and are not.",
 "lede": "There is an attack on payment approvals that costs nothing, requires no malware, and defeats a careful reviewer reading attentively. It exploits the fact that two different strings can render as the same picture.",
 "date": "2024-09-20",
 "category": "Compliance",
 "author_id": "margot-reyes",
 "tags": [
  "homoglyph",
  "Unicode confusables",
  "payee name",
  "canonicalisation",
  "wire fraud",
  "input validation"
 ],
 "image_title": "Homoglyph Payee Attack",
 "schema": "Article",
 "key_takeaways": [
  "Unicode contains many characters that render identically or near-identically to Latin letters. A payee name using them is a distinct string that displays the same.",
  "The Unicode Consortium publishes confusables data specifically for this problem, and almost no financial application consumes it.",
  "Normalisation plus confusable detection at payee creation is a small amount of code and closes the attack at the point of entry."
 ],
 "body": [
  {
   "type": "h2",
   "text": "The mechanism, precisely"
  },
  {
   "type": "diagram",
   "kind": "chain",
   "alt": "A payee is registered with a lookalike character and approved on sight",
   "caption": "No system is compromised. The approver compares two identical-looking strings that differ in bytes.",
   "nodes": [
    {
     "label": "Register payee",
     "sub": "one swapped code point",
     "note": "passes validation"
    },
    {
     "label": "Invoice submitted",
     "sub": "matches the new payee",
     "note": "consistent"
    },
    {
     "label": "Approver compares names",
     "sub": "visually",
     "note": "identical",
     "bad": true
    },
    {
     "label": "Payment released",
     "sub": "to the wrong entity",
     "note": "correctly authorised",
     "bad": true
    }
   ]
  },
  {
   "type": "p",
   "html": "Unicode assigns distinct code points to characters from many scripts. Several of those characters are visually identical, or near-identical, to Latin letters in common fonts."
  },
  {
   "type": "code",
   "text": "Latin small a        U+0061   a\nCyrillic small a     U+0430   а     ← renders identically in most fonts\nGreek small omicron  U+03BF   ο\nLatin small o        U+006F   o     ← near-identical\nFullwidth Latin A    U+FF21   Ａ\nLatin small dotless i U+0131   ı"
  },
  {
   "type": "p",
   "html": "A payee named with a Cyrillic character in place of a Latin one is, to a database, a different vendor. To an approver reading the screen, it is the same vendor."
  },
  {
   "type": "h2",
   "text": "Why this is cheaper than every other payment attack"
  },
  {
   "type": "table",
   "head": [
    "Attack",
    "Requires",
    "Cost"
   ],
   "rows": [
    [
     "Vendor email compromise",
     "Phishing the vendor, persistence, timing",
     "Moderate"
    ],
    [
     "Voice cloning callback",
     "Audio samples, tooling, live performance",
     "Moderate"
    ],
    [
     "Session theft",
     "Malware delivery or proxy phishing infrastructure",
     "Moderate"
    ],
    [
     "<strong style=\"font-weight:600\">Homoglyph payee</strong>",
     "<strong style=\"font-weight:600\">A keyboard</strong>",
     "<strong style=\"font-weight:600\">Zero</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "It also requires no access to your systems beyond the ability to submit a vendor record — through an onboarding portal, a supplier self-service form, or a social-engineered request to AP."
  },
  {
   "type": "h2",
   "text": "Where it lands in the payment flow"
  },
  {
   "type": "p",
   "html": "Two variants, with different targets."
  },
  {
   "type": "ol",
   "items": [
    "<strong style=\"font-weight:600\">Duplicate vendor.</strong> A new payee is created with a name visually identical to an existing supplier and a different bank account. Invoices are submitted against it. Approvers see a familiar name.",
    "<strong style=\"font-weight:600\">Beneficiary name mismatch.</strong> The payee name on the payment differs from the account holder name at the receiving bank in a way that confirmation-of-payee style checks may or may not catch, depending on their normalisation."
   ]
  },
  {
   "type": "h2",
   "text": "What the Unicode Consortium already provides"
  },
  {
   "type": "p",
   "html": "This is a solved problem in specification terms and an unsolved one in deployment terms."
  },
  {
   "type": "p",
   "html": "Unicode Technical Standard 39, on security mechanisms, defines identifier profiles and provides confusables data mapping characters to a skeleton form. Two strings whose skeletons are equal are confusable. Unicode Standard Annex 15 defines normalisation forms — NFC and NFD — that resolve composition differences."
  },
  {
   "type": "p",
   "html": "Together they give a deterministic test. It is not heuristic, it is not machine learning, and it is a few lines of code against a published data file."
  },
  {
   "type": "h2",
   "text": "The control at payee creation"
  },
  {
   "type": "ol",
   "items": [
    "<strong style=\"font-weight:600\">Normalise.</strong> Apply NFC to the submitted payee name. This resolves the case where an accented character is submitted as base plus combining mark.",
    "<strong style=\"font-weight:600\">Restrict the script set.</strong> Decide which scripts are legitimate for your payee population. A business paying only domestic suppliers has no reason to accept mixed-script names.",
    "<strong style=\"font-weight:600\">Compute the skeleton.</strong> Using the published confusables mapping, reduce the name to its skeleton form.",
    "<strong style=\"font-weight:600\">Compare against existing payees.</strong> A skeleton collision with an existing payee is not an error to reject silently — it is a flag for review, because legitimate similar names exist.",
    "<strong style=\"font-weight:600\">Record the decision.</strong> If a reviewer clears a collision, record who cleared it and why."
   ]
  },
  {
   "type": "p",
   "html": "Step two does most of the work at the lowest cost. Mixed-script payee names are almost never legitimate in a domestic payables population, and rejecting them outright removes the attack."
  },
  {
   "type": "h2",
   "text": "Why the approval screen cannot fix this alone"
  },
  {
   "type": "p",
   "html": "Some organisations respond by rendering suspicious characters visibly — highlighting non-Latin characters in payee names at approval time."
  },
  {
   "type": "p",
   "html": "That helps and it is a second line rather than a first. The approver is being asked to notice something at the end of a process where the record should never have been created. Detection at creation is cheaper, earlier and does not depend on an approver's attention at the wrong moment."
  },
  {
   "type": "h2",
   "text": "Signing and canonicalisation"
  },
  {
   "type": "p",
   "html": "There is a subtler point for anyone signing payment statements. If the payee name is part of the signed content, normalisation must happen before signing and must be deterministic, or two systems will compute different hashes for the same logical name."
  },
  {
   "type": "p",
   "html": "Freeze the normalisation form in the specification, apply it before canonicalisation, and reject confusables rather than signing them. A signature over a homoglyph name is a cryptographically perfect record of the wrong payee."
  },
  {
   "type": "h2",
   "text": "The confusable families worth detecting"
  },
  {
   "type": "table",
   "caption": "Four techniques, all cheap",
   "head": [
    "Technique",
    "Example effect"
   ],
   "rows": [
    [
     "Cross-script homoglyphs",
     "Cyrillic а, е, о and Greek ο render as Latin letters"
    ],
    [
     "Zero-width characters",
     "Two names render identically while differing in bytes"
    ],
    [
     "Trailing or doubled whitespace",
     "A name that appears to match exactly"
    ],
    [
     "Directional override characters",
     "Displayed order differs from stored order"
    ]
   ]
  },
  {
   "type": "p",
   "html": "Unicode Technical Standard 39 exists precisely for this and publishes confusable mappings. Running a payee name through skeleton normalisation and comparing against existing payees is a few lines of code and catches every family above."
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“Our payee names come from the vendor master, which is controlled.”</strong> The attack is at creation or amendment of that record, not at payment. Normalise on write and compare against the existing set, not on approval."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“We would spot a different company name.”</strong> The name is not different. That is the entire technique — it renders identically, and the approver has no way to see otherwise."
  }
 ],
 "faq": [
  {
   "q": "Is this a real attack or a theoretical one?",
   "a": "Homoglyph techniques are well documented in domain and identifier attacks. The payee-name variant is the same mechanism applied to a field that is compared visually and stored exactly."
  },
  {
   "q": "Will rejecting mixed-script names break legitimate vendors?",
   "a": "For organisations with international suppliers, yes — which is why the script set should match your actual payee population rather than a default."
  },
  {
   "q": "Does confirmation of payee solve it?",
   "a": "It depends entirely on how the scheme normalises names before matching. Ask your bank what normalisation their matching applies."
  },
  {
   "q": "Where should the check live?",
   "a": "At payee creation, in the vendor master workflow. Detection at approval is a useful second line and a poor first one."
  },
  {
   "q": "Why can't the approver spot it?",
   "a": "The strings render identically. There is nothing visible to spot, which is why the check must be mechanical and must happen on write."
  },
  {
   "q": "Where should the check run?",
   "a": "At payee creation and amendment, against the existing payee set. Checking at approval is too late and asks a human to do a machine's job."
  },
  {
   "q": "Is there a standard for this?",
   "a": "Unicode Technical Standard 39 publishes confusable mappings and a skeleton algorithm designed for exactly this comparison."
  }
 ],
 "sources": [
  {
   "t": "Unicode Technical Standard #39 — Security Mechanisms",
   "u": "https://www.unicode.org/reports/tr39/"
  },
  {
   "t": "Unicode Standard Annex #15 — Normalization Forms",
   "u": "https://www.unicode.org/reports/tr15/"
  },
  {
   "t": "NAIC — life insurance and annuities consumer resources",
   "u": "https://content.naic.org/consumer/life-insurance.htm"
  }
 ],
 "related": [
  {
   "slug": "account-masking-ap-systems-directly-enables-wire",
   "title": "Why account masking enables wire fraud",
   "category": "Compliance"
  },
  {
   "slug": "vendor-bank-account-change-fraud-controls",
   "title": "The vendor invoice scam",
   "category": "Compliance"
  },
  {
   "slug": "cryptographic-receipt-primitive-rfc8785-ed25519-vs-jwt",
   "title": "The cryptographic receipt primitive",
   "category": "Developer"
  },
  {
   "slug": "ethclipper-address-poisoning-defeating-zero-width-space-middle",
   "title": "Address poisoning works because humans check the ends",
   "category": "Crypto"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Homoglyph%20Payee%20Attack/1500_900/blog.jpg",
 "wordcount": 1017,
 "url": "/blog/homoglyphs-unicode-confusables-wire-payees-cheapest-attack-financial.html",
 "reading_time": "5 min read",
 "seo_title": "Homoglyphs and Unicode confusables in payee names",
 "meta_description": "Attackers register payee names using lookalike characters. The database stores distinct strings; the approval screen renders them identically.",
 "hub": {
  "slug": "topics/payment-release-authorization",
  "title": "Payment release authorization"
 },
 "answer": "The database stores two distinct strings; the screen renders them identically. A Cyrillic а and a Latin a occupy different code points and look the same, so an approver comparing two payee names visually is comparing pixels, not data. Normalising and comparing at code-point level catches it; looking never will.",
 "answer_q": "How do lookalike characters defeat a payee approval?",
 "glossary": [
  {
   "term": "Homoglyph",
   "def": "A character from one script that renders identically to one from another."
  },
  {
   "term": "Skeleton",
   "def": "A normalised form under UTS 39 in which confusable characters map to a single representative, so lookalikes collide."
  },
  {
   "term": "Mixed-script name",
   "def": "A string drawing characters from more than one script, which is occasionally legitimate and usually a signal."
  }
 ],
 "checklist": {
  "title": "Detecting confusables in payee data",
  "id": "detect",
  "desc": "Four checks to run on write.",
  "steps": [
   {
    "name": "Normalise to a Unicode skeleton.",
    "text": "UTS 39 confusable mapping, applied at record creation and amendment."
   },
   {
    "name": "Compare against every existing payee.",
    "text": "A skeleton collision with a different underlying string is the signal."
   },
   {
    "name": "Reject or flag mixed-script names.",
    "text": "Unless the vendor legitimately trades under one."
   },
   {
    "name": "Strip and reject invisible characters.",
    "text": "Zero-width, directional overrides and doubled whitespace have no legitimate place in a payee name."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "Manav renders the full beneficiary details and the delta from what was on file, binds the approver's signature to that exact payload, and has the payment service recompute the digest before it releases funds.",
  "href": "../docs.html",
  "label": "See payment gating"
 }
}