编程与开发

不可见字符

无可见字形的字符:空白、零宽字符、控制字符和格式字符,可能引发欺骗和文本隐写等安全问题。

· Updated

What Are Invisible Characters?

Invisible characters are Unicode code points that have no visible glyph — they render as nothing in normal circumstances, yet they occupy space in a string and can affect text layout, rendering, and processing. They include format characters, zero-width characters, and various control or separator code points.

Invisible characters are legitimate and useful in proper typography and internationalization, but they are also exploited for obfuscation, invisible text, and bypassing filters.

Categories of Invisible Characters

Zero-Width Characters

These have no advance width in text layout:

Code Point Name Abbreviation Purpose
U+200B Zero Width Space ZWSP Allows line break without visible space
U+200C Zero Width Non-Joiner ZWNJ Prevents ligature/cursive joining
U+200D Zero Width Joiner ZWJ Joins emoji; enables cursive joining
U+2060 Word Joiner WJ Prevents line break, zero width
U+FEFF Zero Width No-Break Space BOM Historical no-break; now mainly a BOM

Format Characters (Cf)

Code Point Name Effect
U+00AD Soft Hyphen Suggested break point; only visible when line breaks
U+2028 Line Separator Forces line break
U+2029 Paragraph Separator Forces paragraph break
U+200E Left-to-Right Mark LRM: influences bidi algorithm
U+200F Right-to-Left Mark RLM: influences bidi algorithm
U+202A–202E Bidi embedding/override chars Control text direction
U+2061–2064 Mathematical operators Invisible function application, etc.

Non-Printing Control Characters

U+0000–U+001F (C0 controls) and U+007F–U+009F (C1 controls) are mostly invisible and have no standard rendering.

Detecting Invisible Characters

import unicodedata

def is_invisible(char: str) -> bool:
    cat = unicodedata.category(char)
    # Cf = Format, Cc = Control, Cs = Surrogate
    return cat in ("Cf", "Cc") or unicodedata.combining(char) != 0

def find_invisible(text: str) -> list[tuple[int, str, str]]:
    return [
        (i, hex(ord(c)), unicodedata.name(c, "UNKNOWN"))
        for i, c in enumerate(text)
        if is_invisible(c)
    ]

text = "Hello\u200BWorld\u200D!"
find_invisible(text)
# [(5, "0x200b", "ZERO WIDTH SPACE"),
#  (11, "0x200d", "ZERO WIDTH JOINER")]

# Stripping all invisible characters
import regex  # pip install regex
def strip_invisible(text: str) -> str:
    return regex.sub(r"\p{Cf}", "", text)

strip_invisible("Hello\u200BWorld")  # "HelloWorld"

JavaScript Detection

// Detect zero-width and format characters
function findInvisible(text) {
  const results = [];
  for (const [i, char] of [...text].entries()) {
    const cp = char.codePointAt(0);
    if (
      (cp >= 0x200B && cp <= 0x200F) ||  // ZW space, joiners, marks
      (cp >= 0x202A && cp <= 0x202E) ||  // bidi controls
      cp === 0x2060 || cp === 0xFEFF ||
      (cp >= 0x2061 && cp <= 0x2064)
    ) {
      results.push({ index: i, codePoint: cp.toString(16), char });
    }
  }
  return results;
}

// Strip format characters using Unicode property
const stripped = text.replace(/\p{Cf}/gu, "");

Legitimate Uses

# ZWJ in emoji sequences (family emoji)
family = "👨\u200D👩\u200D👧"   # 👨‍👩‍👧 one grapheme

# ZWNJ for Persian/Arabic (prevent unwanted ligature)
correct = "می\u200Cکنم"          # correct word separation in Persian

# LRM/RLM for bidi text
mixed = "Hello \u200Eمرحبا"       # force LTR context around Arabic

Security Considerations

Invisible characters are used for:

  1. Text fingerprinting/watermarking: Embedding hidden patterns to track document leaks.
  2. Bypassing content filters: c\u200Ba\u200Bt to write "cat" while evading text matching.
  3. Homograph attacks: Hidden bidi overrides can reverse text direction in filenames or URLs.
  4. Obfuscating malicious strings: Zero-width characters interspersed in code.
# Security: normalize input by stripping Cf characters
import unicodedata
def sanitize(text: str) -> str:
    # Remove format characters
    cleaned = "".join(c for c in text if unicodedata.category(c) != "Cf")
    # NFC normalize
    return unicodedata.normalize("NFC", cleaned)

Quick Facts

Property Value
Most common invisible chars U+200B, U+200C, U+200D, U+2060, U+FEFF
Unicode category Cf (Format), Cc (Control)
Emoji ZWJ U+200D — joins emoji into multi-person sequences
Python detection unicodedata.category(c) == "Cf"
JS regex removal text.replace(/\p{Cf}/gu, "")
Security risk Homograph attacks, filter bypass, text fingerprinting
Legitimate uses Bidi control, emoji sequences, typography, cursive joining

相关术语

编程与开发 中的更多内容