Ungültiger Base64-String: Warum das Decodieren scheitert und wie Sie es beheben
Ein Base64-Decoder lehnt einen String aus einem von drei Gründen ab: ein Zeichen außerhalb des Alphabets, eine Länge, die nicht stimmen kann, oder Padding an der falschen Stelle.
Base64 verwendet 64 Zeichen, nämlich A bis Z, a bis z, 0 bis 9, + und /, dazu = als Padding am Ende (RFC 4648, Abschnitt 4). Alles andere lässt einen strengen Decoder scheitern. Verwirrend ist, dass sich die Decoder nicht einig sind, was mit „alles andere“ gemeint ist: Ein Base64URL-String wie PDw_Pz8-Pg== wird von Node.js Buffer korrekt decodiert, scheitert in .NET und wird in Python stillschweigend zu falschen Bytes. Wer weiß, was der eigene Decoder toleriert, hat das Problem schon fast gelöst.
Der Fehler in den einzelnen Sprachen
| Wo | Was Sie erhalten |
|---|---|
Chrome und Edge, atob() | InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded. |
Node.js, atob() | InvalidCharacterError: Invalid character, oder bei falscher Länge The string to be decoded is not correctly encoded. |
Python, base64.b64decode() | binascii.Error: Incorrect padding, Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4 oder, mit validate=True, Only base64 data is allowed |
.NET, Convert.FromBase64String() | FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. |
Java, Base64.getDecoder().decode() | IllegalArgumentException: Illegal base64 character, gefolgt vom Zeichencode in Hex, etwa 2d für - |
PHP, base64_decode($s, true) | Keine Meldung: Die Funktion gibt false zurück |
| AWS CLI Version 2 | Invalid base64:, gefolgt von dem Wert, den Sie übergeben haben |
Node.js Buffer.from(s, 'base64') fällt aus der Reihe: Es wirft nie eine Ausnahme. Zeichen, die es nicht kennt, überspringt es einfach, sodass ein beschädigter String ohne jede Fehlermeldung beschädigte Bytes liefert.
Was die einzelnen Decoder tolerieren
| Eingabe | atob | Python b64decode | .NET | PHP strict |
|---|---|---|---|---|
Fehlendes Padding: SGVsbG8 | Decodiert | Incorrect padding | Fehler | Decodiert |
Zu viel Padding: SGVsbG8== | Fehler | Decodiert | Fehler | Gibt false zurück |
| Leerzeichen oder Zeilenumbrüche | Ignoriert | Ignoriert (Fehler mit validate=True) | Ignoriert | Ignoriert |
Base64URL-Zeichen - und _ | Fehler | Still verworfen: falsche Bytes oder ein Fehler | Fehler | Gibt false zurück |
Andere Zeichen: Anführungszeichen, \, !, : | Fehler | Still verworfen | Fehler | Gibt false zurück |
Der Standardmodus von Python verdient eine Warnung. Er verwirft vor dem Decodieren jedes Zeichen außerhalb des Alphabets, sodass die Buchstaben eines Präfixes wie data:image/jpeg;base64, so decodiert werden, als wären sie Daten. Je nach Länge des Präfixes erhalten Sie eine verwirrende Meldung über die Anzahl der Zeichen oder gar keinen Fehler und dafür einige Bytes Datenmüll vor dem eigentlichen Inhalt. Übergeben Sie validate=True, wenn fehlerhafte Eingaben klar scheitern sollen.
Zeichen, die nicht dazugehören
Meldet ein strenger Decoder ein ungültiges Zeichen, hat der String fast immer auf dem Weg zu Ihnen etwas aufgesammelt:
- Anführungszeichen oder umgebender Text. Wer
"SGVsbG8="samt Anführungszeichen kopiert oder gleich eine ganze Zeile"key": "value", fügt Zeichen hinzu, die kein Base64 sind. - Ein Data-URI-Präfix. In
data:image/png;base64,iVBOR...ist nur der Teil nach dem Komma Base64. Entfernen Sie alles bis einschließlich des Kommas. - JSON-Escapes. PHPs
json_encodeschreibt/als\/, und umbrochene Inhalte wie ein PEM-Zertifikat werden mit\nzwischen den Zeilen gespeichert. Kopieren Sie den rohen JSON-Text statt des geparsten Werts, kommen die Backslashes mit. Parsen Sie zuerst das JSON und decodieren Sie dann den resultierenden String. - Base64URL-Zeichen.
-und_bedeuten, dass der String das URL-sichere Alphabet verwendet, wie es bei JWTs der Fall ist. Mehr dazu im Abschnitt weiter unten. - Unsichtbare Zeichen. Ein geschütztes Leerzeichen oder ein Leerzeichen der Breite null, eingefügt aus einem Chat-Programm oder einem Dokument, ist kein ASCII-Leerraum. Selbst
atob, das gewöhnliche Leerzeichen ignoriert, lehnt es deshalb ab.
Ein Fall erzeugt überhaupt keinen Fehler. Landet ein Standard-Base64-Wert ohne Prozentkodierung in einem Query-String, wird beim Parsen der Query jedes + zu einem Leerzeichen. atob und Python überspringen diese Leerzeichen dann: ++++YWJj kommt als YWJj an und wird zu abc decodiert, die ersten drei Bytes sind stillschweigend verschwunden. Kodieren Sie den Wert mit encodeURIComponent oder verwenden Sie in URLs Base64URL.
Falsche Länge und falsches Padding
Base64 macht aus je 3 Bytes 4 Zeichen, ein String mit Padding hat also immer eine durch 4 teilbare Länge. Ohne Padding hat die letzte Gruppe 2 oder 3 Zeichen. Ein Rest von 1 ist bei keiner Eingabe möglich: Gilt length % 4 === 1, ist ein Zeichen verloren gegangen oder eines hinzugekommen, und kein noch so passendes Padding kann das reparieren.
Padding darf nur am Ende stehen, und es sind nie mehr als zwei =-Zeichen. RFC 4648 schreibt Padding vor, sofern das Format, das Base64 verwendet, nichts anderes festlegt – und mehrere Formate tun genau das: JWTs und PKCE-Code-Challenges lassen es weg. So ergänzen Sie fehlendes Padding, bevor Sie den String an einen strengen Decoder übergeben:
// JavaScript
const padded = s + '='.repeat((4 - s.length % 4) % 4);
# Python
padded = s + '=' * (-len(s) % 4)
Base64URL in einem Standard-Decoder
Base64URL (RFC 4648, Abschnitt 5) ersetzt + durch - und / durch _ und lässt das Padding meist weg. Ein Standard-Decoder lehnt beide Zeichen ab, scheitern aber nur Strings, die zufällig eines davon enthalten – deshalb wirkt der Fehler willkürlich. Ein zufälliger 16-Byte-Wert wird zu 22 Base64URL-Zeichen, von denen 21 ein - oder _ sein können. Jedes davon vermeidet beide mit der Wahrscheinlichkeit 62/64, also werden (62/64) hoch 21, etwa 51 Prozent, solcher Werte problemlos decodiert, und die übrigen 49 Prozent scheitern. Wandeln Sie das Alphabet um und ergänzen Sie das Padding, oder verwenden Sie einen Decoder, der für die Variante gebaut ist:
// Browser
const b64 = s.replace(/-/g, '+').replace(/_/g, '/');
const bytes = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
// Node.js
Buffer.from(s, 'base64url');
# Python
base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))
// Java
Base64.getUrlDecoder().decode(s);
Auch Pythons urlsafe_b64decode braucht das Padding, deshalb ergänzt es das Beispiel. Die Unterschiede zwischen den beiden Alphabeten erklärt der Guide Base64 vs. Base64URL.
Zeilenumbrüche aus E-Mails und PEM-Dateien
MIME-E-Mails umbrechen Base64 nach 76 Zeichen pro Zeile, PEM-Dateien wie Zertifikate nach 64. atob, .NET und PHP überspringen die Zeilenumbrüche. Der einfache Decoder von Java tut das nicht: Verwenden Sie für umbrochene Eingaben Base64.getMimeDecoder(). Entfernen Sie bei PEM außerdem die Zeilen -----BEGIN ...----- und -----END ...-----, denn sie sind kein Base64.
Decodiert, aber das Ergebnis stimmt nicht
- Die Ausgabe ist wieder Base64. Die Daten wurden zweimal codiert.
YUdWc2JHOD0=wird zuaGVsbG8=decodiert und das wiederum zuhello. Decodieren Sie einfach noch einmal. - Buchstaben mit Akzent oder Umlaut erscheinen als Zeichenpaare wie
é.atobliefert ein Zeichen pro Byte, UTF-8-Text braucht also einen zweiten Schritt:new TextDecoder().decode(Uint8Array.from(atob(s), c => c.charCodeAt(0))). - Python wirft nach dem Decodieren
UnicodeDecodeError. Die Bytes sind gültiges Base64, aber kein UTF-8-Text, zum Beispiel'utf-8' codec can't decode byte 0xe9bei Text, der als Latin-1 gespeichert wurde. Decodieren Sie mit der richtigen Zeichenkodierung, etwa.decode('cp1252'), oder behandeln Sie das Ergebnis als Binärdaten. - Die Ausgabe besteht aus unlesbaren Symbolen. Es handelt sich um Binärdaten, etwa ein Bild, ein PDF oder komprimierte Daten. Speichern Sie die Bytes in einer Datei, statt sie auszugeben.
AWS CLI: „Invalid base64“
Die AWS CLI Version 2 erwartet binäre Parameter standardmäßig als Base64. Übergeben Sie einem Befehl wie aws lambda invoke --payload '{"id": 1}' einfaches JSON, scheitert er mit Invalid base64. Ergänzen Sie --cli-binary-format raw-in-base64-out, um den Text unverändert zu übergeben, oder codieren Sie den Payload selbst.
Einen String schnell prüfen
Fügen Sie den Wert in Base64 decodieren ein. Das Werkzeug entfernt Leerraum und ein Data-URI-Präfix, wandelt Base64URL-Zeichen um und ergänzt fehlendes Padding, bevor es decodiert. Meldet es danach immer noch, die Eingabe sei kein gültiges Base64, enthält der String wirklich ein fremdes Zeichen oder hat eine unmögliche Länge. Gelingt das Decodieren, wird UTF-8-Text direkt angezeigt, alles andere, etwa ein Bild, zum Herunterladen als Datei angeboten.