Base64 vs. Base64URL: Was sich ändert und wann Sie welche Variante nutzen

Base64URL ist Standard-Base64 mit zwei ausgetauschten Zeichen, - für + und _ für /, und meist ohne das =-Padding.

Direkt ausprobieren: Base64 Encoder & Decoder JWT-Decoder

Beide Codierungen sind in RFC 4648 definiert: Standard-Base64 in Abschnitt 4 und die Variante „URL and filename safe“, base64url, in Abschnitt 5. Sie teilen Daten auf genau dieselbe Weise in 6-Bit-Gruppen auf und haben 62 ihrer 64 Zeichen gemeinsam. Nur die Zeichen für die Werte 62 und 63 unterscheiden sich, dazu die Konvention beim Padding. Deshalb ist die Umwandlung zwischen beiden ein reiner Zeichentausch, ohne die Daten neu zu codieren – und deshalb ist eine Verwechslung ein leichter Fehler, der oft lange unbemerkt bleibt.

Die beiden Alphabete im Vergleich

Base64 (RFC 4648, Abschnitt 4)Base64URL (RFC 4648, Abschnitt 5)
Werte 0 bis 61A-Z a-z 0-9A-Z a-z 0-9
Wert 62+-
Wert 63/_
Padding mit =Vorgeschrieben, sofern das nutzende Format nichts anderes festlegtMeist weggelassen
Unverändert sicher in URLs und DateinamenNeinJa

Dieselben Bytes in beiden Formen:

Bytes       fb ff bf
Base64      +/+/
Base64URL   -_-_

Text        <<???>>
Base64      PDw/Pz8+Pg==
Base64URL   PDw_Pz8-Pg

Codierte Binärdaten wie Hashes, Schlüssel und zufällige Tokens enthalten sehr häufig irgendwo ein + oder /. Der Unterschied ist also kein Randfall. RFC 4648 sagt selbst, dass base64url nicht als dieselbe Codierung wie base64 betrachtet werden sollte und Code die erwartete Variante ausdrücklich benennen sollte.

Warum Standard-Base64 in URLs und Dateinamen Probleme macht

Jedes der drei zusätzlichen Zeichen von Standard-Base64 hat anderswo eine eigene Bedeutung:

  • + wird als Leerzeichen gelesen, wenn ein Query-String als Formulardaten decodiert wird, und genau das tun URLSearchParams, PHPs $_GET und die meisten Server-Frameworks. Ein Token wie ab+c kommt als ab c an.
  • / trennt Pfadsegmente. Ein Base64-Wert im Pfad erzeugt also zusätzliche Segmente, und manche Webserver lehnen die codierte Form %2F in Pfaden ab. In Dateinamen ist das Zeichen unter Windows, macOS und Linux ebenfalls nicht erlaubt.
  • = trennt den Namen eines Parameters von seinem Wert.

Den Wert zu prozentkodieren (%2B, %2F, %3D) funktioniert, solange jedes beteiligte Stück Code das genau einmal tut. In der Praxis vergisst es eine Schicht oder erledigt es doppelt. Deshalb verwenden Formate, die für URLs gedacht sind, Base64URL und kommen ganz ohne Maskierung aus.

Regeln für das Padding

Base64 codiert 3 Bytes als 4 Zeichen. Ist die Datenlänge kein Vielfaches von 3, fällt die letzte Gruppe kürzer aus, und Standard-Base64 füllt sie mit = auf 4 Zeichen auf:

Bytes in der letzten GruppeBase64Base64URL ohne Padding
1, zum Beispiel ff/w==_w
2, zum Beispiel fb ff+/8=-_8
3, zum Beispiel fb ff bf+/+/-_-_

Abschnitt 3.2 von RFC 4648 schreibt Padding vor, sofern die Spezifikation, die sich darauf bezieht, nicht ausdrücklich etwas anderes sagt. Abschnitt 5 ergänzt, dass = in einer URL normalerweise prozentkodiert werden müsste und dass das Padding entfallen kann, wenn die Länge der Daten ohnehin bekannt ist. Formate, die auf Base64URL aufbauen, nutzen diese Möglichkeit: JSON Web Signatures (RFC 7515) und damit auch JWTs entfernen alle abschließenden =, ebenso PKCE-Code-Challenges (RFC 7636).

Ohne Padding ergeben n Bytes ⌈4n/3⌉ Zeichen: 16 Bytes werden zu 22 statt 24 Zeichen, 32 Bytes zu 43 statt 44. Eine Länge, die bei der Division durch 4 einen Rest von 1 lässt, ist in keiner der beiden Varianten gültig.

Wo welche Variante verwendet wird

KontextVariante
JWT-Header, -Payload und -SignaturBase64URL ohne Padding
Werte in JSON Web Keys wie n und eBase64URL ohne Padding
OAuth PKCE code_challengeBase64URL ohne Padding
Data-URIs, data:image/png;base64,...Standard-Base64 (RFC 2397)
E-Mail-Anhänge (MIME)Standard-Base64, Zeilen mit höchstens 76 Zeichen (RFC 2045)
PEM-Zertifikate und -SchlüsselStandard-Base64, Zeilen mit 64 Zeichen (RFC 7468)
Header für HTTP Basic AuthenticationStandard-Base64 (RFC 7617)
Tokens in URLs, Dateinamen, Cache-SchlüsselBase64URL ist die sichere Wahl

Die Standardeinstellungen der Bibliotheken folgen Standard-Base64: btoa, Pythons b64encode, PHPs base64_encode und Javas Base64.getEncoder() erzeugen allesamt +, / und Padding. Base64URL müssen Sie ausdrücklich anfordern.

Zwischen den Varianten umwandeln

atob und btoa kennen nur Standard-Base64. Mit ihnen tauschen Sie die Zeichen also selbst aus und korrigieren das Padding von Hand:

// Base64 -> Base64URL
const url = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

// Base64URL -> Base64
let std = url.replace(/-/g, '+').replace(/_/g, '/');
std += '='.repeat((4 - std.length % 4) % 4);

Aktuelle Browser bieten außerdem Uint8Array.prototype.toBase64({ alphabet: 'base64url', omitPadding: true }) und Uint8Array.fromBase64(s, { alphabet: 'base64url' }), die direkt mit Bytes arbeiten. Prüfen Sie die Unterstützung in Ihren Zielbrowsern, bevor Sie sich darauf verlassen. Andere Plattformen bieten direkte Unterstützung oder ein kurzes Idiom:

// Node.js
Buffer.from(data).toString('base64url');     // no padding
Buffer.from(s, 'base64url');

# Python
base64.urlsafe_b64encode(data).rstrip(b'=')
base64.urlsafe_b64decode(s + '=' * (-len(s) % 4))

// PHP
rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
base64_decode(strtr($s, '-_', '+/'), true);

// Java
Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
Base64.getUrlDecoder().decode(s);

Pythons urlsafe_b64decode lehnt Eingaben ohne Padding ab, deshalb werden die =-Zeichen ergänzt. PHPs base64_decode akzeptiert fehlendes Padding, selbst im Strict-Modus.

Fallstricke

  • Fehler, die Tests bestehen. Ein Standard-Decoder scheitert nur an Base64URL-Eingaben, die - oder _ enthalten. Bei Zufallsdaten ist das etwa die Hälfte aller 16-Byte-Werte und etwa drei Viertel aller 32-Byte-Werte, zum Beispiel SHA-256-Digests (1 − (62/64) hoch 42 ≈ 74%). Ein Test mit ein oder zwei Beispielwerten kann also leicht bestehen. Warum Decoder solche Strings ablehnen und wie unterschiedlich die einzelnen Sprachen reagieren, erklärt der Guide Ungültiger Base64-String.
  • Tolerante Decoder verbergen die Verwechslung. Node.js Buffer.from(s, 'base64') akzeptiert beide Alphabete. Das ist bequem, bedeutet aber, dass der Fehler erst in einem anderen Dienst auftaucht. Pythons b64decode verwirft im Standardmodus - und _ stillschweigend und liefert dann falsche Bytes oder eine irreführende Meldung über die Anzahl der Zeichen.
  • Base64URL ist keine URL-Kodierung. Wer einen Standard-Base64-String prozentkodiert, erhält %2B, %2F und %3D. Daraus wird kein Base64URL, und ein Decoder, der die eine Form erwartet, akzeptiert die andere nicht.
  • Keine der beiden Varianten verbirgt etwas. Der Payload eines JWT ist lediglich Base64URL-codiertes JSON, lesbar für jeden, der das Token besitzt. Fügen Sie eines in den JWT Decoder ein, um Header und Payload zu sehen.

Der Base64 Encoder auf dieser Website erzeugt Standard-Base64 mit Padding; brauchen Sie Base64URL, wenden Sie die Ersetzung von oben an. Zum Decodieren akzeptiert Base64 decodieren beide Alphabete und ergänzt fehlendes Padding selbst, Sie können also jede der beiden Formen einfügen.

Häufige Fragen

Was ist der Unterschied zwischen Base64 und Base64URL?

Base64URL ersetzt + durch - und / durch _, damit das Ergebnis in URLs und Dateinamen sicher ist, und lässt das =-Padding meist weg. Die übrigen 62 Zeichen und die Aufteilung der Daten in 6-Bit-Gruppen sind identisch.

Kann ein normaler Base64-Decoder Base64URL lesen?

Nur nach einer Umwandlung: - durch + und _ durch / ersetzen, dann = anhängen, bis die Länge ein Vielfaches von 4 ist. Manche Decoder, etwa Node.js Buffer, akzeptieren beide Alphabete direkt.

Warum verwenden JWTs Base64URL ohne Padding?

Tokens reisen in URLs, HTTP-Headern und Cookies, wo +, / und = Probleme verursachen. RFC 7515 definiert die Codierung für JSON Web Signatures als base64url, bei dem alle abschließenden =-Zeichen entfernt werden.

Ist Base64URL länger als Base64?

Nein. Beide erzeugen gleich viele Zeichen pro Byte; ohne Padding ist Base64URL bis zu zwei Zeichen kürzer.
Direkt ausprobieren: Base64 Encoder & Decoder JWT-Decoder