Caractères Unicode zero-width
Unicode contient plusieurs caractères de largeur nulle : ils existent dans le texte, ils ont un code point, mais ils ne prennent aucune place à l'affichage. Initialement conçus pour le contrôle du rendu typographique (ligatures, coupure de mots), ils se révèlent parfaits pour la stéganographie.
| Code Point | Nom | Usage légitime | Stégo |
|---|---|---|---|
| U+200B | ZERO WIDTH SPACE | Coupure de mot | bit 0 |
| U+200C | ZERO WIDTH NON-JOINER | Empêche ligature | bit 1 |
| U+200D | ZERO WIDTH JOINER | Force ligature | séparateur |
| U+FEFF | ZERO WIDTH NO-BREAK SPACE | BOM / no-break | marqueur |
| U+2060 | WORD JOINER | Empêche saut de ligne | bit alternatif |
Encodage binaire
On convertit chaque caractère du message secret en binaire 8 bits (ASCII/UTF-8), puis on remplace chaque 0 par U+200B et chaque 1 par U+200C. La séquence de caractères invisibles est insérée dans le texte de couverture. Un octet nul (8× U+200B) marque la fin du message.
MESSAGE : "Hi"
'H' = ASCII 72 = 01001000
'i' = ASCII 105 = 01101001
Encodage zero-width :
0 → (U+200B)
1 → (U+200C)
Résultat pour "Hi" :
(0 1 0 0 1 0 0 0) (0 1 1 0 1 0 0 1)
Texte de couverture : "Bonjour tout le monde"
Résultat : "B[hidden chars]onjour tout le monde"
↑
72 caractères invisibles insérésVariantes & techniques avancées
Homoglyphes
AvancéRemplacer des lettres par des caractères Unicode visuellement identiques. Ex: "a" latin (U+0061) vs "а" cyrillique (U+0430). Détection très difficile.
Whitespace encoding
SimpleEncoder des bits via des espaces (U+0020) et des tabulations (U+0009) à la fin des lignes. Simple mais détectable visuellement dans certains contextes.
Typosquatting Unicode
AvancéSubstituer des caractères par leurs variantes Unicode (majuscules, italiques mathématiques). Invisible à l'affichage mais change les codes points.
Distribution dans le texte
ModéréDistribuer les caractères ZW après chaque mot plutôt qu'en bloc. Réduit la signature statistique et l'impact en cas de troncature du texte.
Détection & limitations
Les caractères zero-width sont détectables dans plusieurs contextes :
- 01
Éditeur hexadécimal ou analyseur Unicode (ex: unicode.org character inspector)
- 02
Regex : /[\u200B\u200C\u200D\uFEFF]/g → retourne tous les ZW chars
- 03
Copier-coller dans un éditeur de code (VS Code, Vim) : surligne les chars suspects
- 04
len() en Python sur la chaîne : la longueur dépasse le nombre de chars visibles
- 05
Systèmes de modération de contenu filtrant les ZW chars
// Détecter des caractères zero-width dans une chaîne
function hasHiddenChars(text) {
return /[]/.test(text)
}
// Compter les chars cachés
function countHidden(text) {
return (text.match(/[]/g) || []).length
}
// Nettoyer un texte
function stripHidden(text) {
return text.replace(/[]/g, '')
}⚠ Certaines plateformes (Twitter/X, Discord, Slack) filtrent automatiquement les caractères zero-width. Le message serait détruit à la transmission. Préférer un canal de communication qui n'effectue pas de sanitisation du texte.
Cas d'usage
Watermarking de documents
Identifier l'origine d'une fuite en marquant chaque copie différemment. Chaque destinataire reçoit un texte légèrement différent.
Communication covert
Transmettre un message secret dans un email ou message d'apparence anodine.
CTF & challenges
Fréquemment utilisé dans les CTF pour cacher des flags dans des textes apparemment normaux.
Recherche en sécurité
Étudier la robustesse des systèmes de modération et de filtrage de contenu.
Questions fréquentes
Comment quelqu'un peut-il cacher un message dans du texte sans que ça se voie ?
En utilisant des caractères Unicode qui existent dans le fichier texte mais ne s'affichent pas. Le caractère U+200B (Zero Width Space) prend zéro pixel à l'écran mais est bien présent dans le code. En alternant U+200B (bit 0) et U+200C (bit 1), on peut encoder n'importe quel message binaire dans un texte d'apparence totalement normale.
Est-ce que ça fonctionne partout ? Email, WhatsApp, Twitter ?
Non. Certaines plateformes nettoient automatiquement les caractères zero-width : Twitter/X, Discord et Slack les filtrent. Les emails HTML peuvent les préserver. WhatsApp les supprime souvent. Pour que la technique fonctionne, le canal doit transmettre le texte tel quel, sans sanitisation Unicode. Les fichiers texte bruts et les sites web qui n'assainissent pas les entrées fonctionnent bien.
À quoi sert concrètement la stéganographie textuelle ?
Principalement au watermarking de documents : chaque employé qui reçoit un document confidentiel en reçoit une version avec un identifiant unique invisible. En cas de fuite, on analyse le texte pour identifier qui l'a divulgué. C'est aussi utilisé dans les CTF (Capture The Flag) pour cacher des flags dans des textes apparemment normaux.
Comment décoder un texte qui contient des caractères zero-width ?
En collant le texte dans un éditeur hexadécimal ou dans notre outil de décodage. On peut aussi utiliser JavaScript : `text.replace(/[^\u200B\u200C]/g, '')` extrait uniquement les caractères cachés, puis on interprète U+200B comme 0 et U+200C comme 1 pour reconstruire le message binaire en texte ASCII.
Est-ce détectable ? Un lecteur humain peut-il s'en rendre compte ?
À l'œil nu, absolument pas — le texte est visuellement identique. Mais techniquement, c'est facilement détectable : copier-coller dans VS Code ou Vim surligne les caractères suspects, `len()` en Python retourne plus que le nombre de caractères visibles, et une simple regex `/[\u200B-\u200D\uFEFF]/g` détecte les caractères cachés. Ce n'est donc pas une technique adaptée contre un adversaire technique.
Tester la technique
Encodez et décodez un message dans du texte directement dans votre navigateur.