
Umgeht LLM-Textwasserzeichen durch Einfügen von Unicode-Variationsselektoren; enthält den SynthID-Generator, den mean-g-Detektor, Normalisierungsabwehrmaßnahmen und Entropie-Experimente.

Ja, aber nur eine Angriffsfamilie funktioniert, und es ist nicht die, die alle annehmen.
Unicode-Variationsselektoren (Kategorie Mn, U+FE00 bis U+FE0F und U+E0100 bis U+E01EF) treiben den Detektor unter die Schwelle und bleiben dort. Jeder andere Angriff mit unsichtbaren Zeichen, den ich ausprobiert habe, wird durch eine einzige Zeile der Eingabenormalisierung vollständig rückgängig gemacht. Variationsselektoren nicht, weil sie bedeutungstragende Codepoints sind (Emoji-Präsentation, CJK-Varianten), die NFKC nicht und auch nicht wegfalten sollte.
Über Modelle und Domänen hinweg repliziert:
| Modell | Domäne | Baseline-z | nach vs16_30 | Änderungsrate |
|---|---|---|---|---|
| gpt-oss-20b | Prosa | 45,03 | 0,72 | 57% |
| gpt-oss-20b | Code | 37,24 | 0,68 | 58% |
| Qwen3.8-27B | Prosa | 35,50 | -0,67 | 57% |
Die Schwelle liegt bei z = 2,33. Alle drei landen nach dem Angriff darunter und bleiben auch nach der Normalisierung darunter (0,09, 0,45, -0,78). Der Text wird für einen menschlichen Leser identisch dargestellt.
Der zweite echte Befund braucht überhaupt keinen Angriff: Text mit geringer Entropie wird von vornherein kaum mit einem Wasserzeichen versehen. Mit deaktiviertem Thinking erreicht die reine Code-Ausgabe von Qwen3.8-27B ohne Angriff z = 4,31, nahe an der Schwelle von 2,33, ohne dass irgendetwas damit gemacht wurde. Schaltet man Thinking wieder ein, erreicht dasselbe Modell, dieselbe Domäne, dieselbe Länge z = 25,53, weil die Reasoning-Präambel gewöhnliche Prosa ist und die Markierung normal trägt.
Alles in diesem Repo wurde an diesen Modellen gemessen, an nichts anderem:
| Modell | Parameter | Wo | Was ausgeführt wurde |
|---|---|---|---|
| Qwen3.5-0.8B | 0,8B | Mac (MPS) | vollständige Angriffsleiter, 1k-8k |
| Qwen3.5-4B | 4B | Mac / 4090 | vollständige Angriffsleiter + das Entropie-Experiment + Stego roh/normalisiert |
| DeepSeek-R1-Distill-Qwen-14B | 14B | Mac (MLX, 4-Bit) | Wasserzeichen + Erkennung, Reasoning-Traces |
| gpt-oss-20b | 20B | 3090 / 4090 / RunPod | vollständige Angriffsleiter bis 32k, Prosa + Code, Variationsselektoren, Skalierungsgesetz der Einfügerate bis 131k |
| Qwen3.8-27B | 27B | RunPod H100 / Modal H100 | Prosa + Code, Variationsselektoren, Thinking-Modus-Kontrast |
| gpt-oss-120b | 120B | Modal H100 (MXFP4) / RunPod H100 | Prosa + Code + Reasoning bei 2k / 8k / 32k, partielle Skalierung der Einfügerate bis 65k |
| DeepSeek-V4-Flash | 284B MoE | RunPod 2x H200 (FP8) | Prosa + Code bei 2k / 8k |
Nicht jedes Experiment lief auf jedem Modell, und die Tabellen unten geben an, was wo lief. Größere Modelle wurden hinzugefügt, als sich die interessanten Fragen eingrenzten, sodass die Angriffsleiter bei den kleinen Modellen breit ist und die Arbeit zu Länge und Domäne sich auf die großen konzentriert.
Anthropic (und vor ihnen Google DeepMind, das SynthID-Text-Paper) versieht generierten Text mit einem Wasserzeichen, indem die Token-Sampling mit einem schlüsselbasierten Turnier verzerrt wird. Das Signal liegt darin, welche Token ausgewählt wurden, nicht in irgendeinem versteckten Zeichen. Ich wollte wissen, wie fragil das für eine motivierte Person mit Suchen-und-Ersetzen ist, also habe ich den Generator plus einen untrainierten Mean-g-Detektor gebaut und Angriffe darauf geworfen.
Die Detektor-Schwelle liegt bei z = 2,33 (1% Falsch-Positiv-Rate). Darüber = Wasserzeichen erkannt. Ich habe mit Wasserzeichen versehenen Text generiert, ihn angegriffen, neu tokenisiert und bewertet. Jede Ergebnistabelle enthält eine roundtrip-Kontrolle (nicht angegriffener Text mit Wasserzeichen), damit man sieht, dass der Detektor in dieser Zeile tatsächlich funktioniert. Zweimal in dieser Studie ließ ein Bug jedes z auf null kollabieren, und die Kontrolle war das Einzige, was das aufdeckte.
Die Angriffsfläche teilt sich sauber nach Unicode-Kategorie, und das war letztlich die ganze Geschichte.
Funktioniert (Kategorie Mn, nicht raumgreifende Markierungen):
| Angriff | Was er tut | Änderungsrate | Übersteht Normalisierung? |
|---|---|---|---|
vs16_30 | Variationsselektor nach ~30% der Zeichen | 57% | ja |
vs16 | Variationsselektor nach ~10% der Zeichen | 23% | ja (z 3,46) |
vs_supp | Selektoren der Supplementary Plane (U+E0100+) | 24% | ja (z 3,40) |
homoglyph | Kyrillisches а für lateinisches a (Kategorie Ll) | 9% | ja, aber schwacher Effekt |
Funktioniert nicht (Kategorie Cf, Formatierungszeichen):
| Angriff | rohes z | normalisiertes z | Urteil |
|---|---|---|---|
zwsp_30 | -0.09 | 35.68 | vollständig rückgängig gemacht |
combo | 0.92 | 35.68 | vollständig rückgängig gemacht |
bidi | 24.37 | 35.68 | vollständig rückgängig gemacht |
nbsp | 41.04 | 46.51 | bewegt es kaum |
Cf-Zeichen sind reine Formatierung ohne semantischen Inhalt, sodass ein Normalisierer sie vollständig entfernen kann, ohne etwas zu verlieren. Mn-Variationsselektoren tragen in Emoji- und CJK-Text echte Bedeutung, sodass ein pauschales Entfernen legitime Dokumente beschädigen würde. Diese Asymmetrie ist der Grund, warum eine Familie vertretbar ist und die andere nicht.
Der praktische Umfang: Damit eine starke Baseline unter den Schwellenwert gedrückt wird, müssen etwa 20 % oder mehr der Zeichen verändert werden, aber die eingefügten Zeichen sind unsichtbar, sodass der Text bei jeder Änderungsrate normal lesbar bleibt. Es ist visuell als identisch verifiziert, siehe src/fidelity_check.py. Vier Familien fügen nur Zeichen ein, die nichts darstellen; Homoglyphen sind die ehrliche Ausnahme (schriftartabhängig, nicht garantiert unsichtbar).
Meine anfängliche Befürchtung war die langweilige: dass Gedankenstriche und „and" zu „&" und das Entfernen von Markdown einfach funktionieren würden und das Wasserzeichen sich als Sicherheitstheater herausstellen würde. Viele Leute online gehen genau davon aus.
Es funktioniert nicht. Die gesamte Leiter der Oberflächenbearbeitung auf gpt-oss-20b, bis zu 32k Token:
| Angriff | Änderungsrate | z @ 1k | z @ 32k |
|---|---|---|---|
| roundtrip (Kontrolle) | 0% | 25.7 | 104.3 |
| Gedankenstrich zu Bindestrich | ~0% | 26.4 | 113.7 |
| sämtliches Markdown entfernen | 13.6% | 27.2 | 103.3 |
| AmE zu BrE + Abkürzungen | 1.3% | ~28 | ~100 |
| 40 % jedes Wortes löschen | 38% | 4.9 | 25.4 |
Über die drei Modelle, auf denen diese Leiter lief (0.8B, 4B, 20B = 294 Zellen), überschritt genau eine Zelle den Schwellenwert: das Löschen von 39 % jedes Wortes bei der kürzesten Länge auf dem 4B. Das ist kein Angriff, das ist das Zerstückeln des Dokuments.
Insgesamt gemessene Zellen über die gesamte Studie: 440 (294 aus der obigen Angriffsleiter, plus 146 aus dem untenstehenden Scaling-Law-Sweep, der die Prosa auf 131072 Token erweitert).
Zwei Dinge haben mich überrascht: