Datei hochladen
  1. base64hub
  2. Base64 verstehen
  3. Base64 vs. Base64URL
Anleitung

Base64 vs. Base64URL: Alphabete, Padding, Fallstricke

Base64URL ist Base64 mit zwei geänderten Zeichen, damit die Ausgabe unverändert in einer URL oder einem Dateinamen stehen kann. Die Bytes sind identisch; die Alphabete unterscheiden sich an zwei Stellen.

Der Unterschied in einer Tabelle

Beide Varianten sind in RFC 4648 definiert. Abschnitt 4 beschreibt Standard-Base64, Abschnitt 5 das Alphabet „URL and Filename safe“. Sie teilen 62 Zeichen und denselben Algorithmus; nur die Indizes 62 und 63 unterscheiden sich.

Standard-Base64 (RFC 4648 §4)Base64URL (RFC 4648 §5)
Index 0–61A–Z a–z 0–9gleich
Index 62+-
Index 63/_
Padding= standardmäßig Pflichtmeist weggelassen
Unbedenklich in URL-Pfad oder Queryneinja
Unbedenklich als Dateinamenein (/ trennt Pfade)ja

Hier dieselbe Eingabe auf beide Arten kodiert:

EingabeStandardBase64URLBase64URL ohne Padding
<<???>>PDw/Pz8+Pg==PDw_Pz8-Pg==PDw_Pz8-Pg
Bytes fb ff+/8=-_8=-_8
ManTWFuTWFuTWFu

Die letzte Zeile erinnert daran, dass viele Zeichenketten in beiden Varianten gleich aussehen. Unterscheiden lässt sich eine Zeichenkette nur, wenn sie +, /, - oder _ enthält oder das Padding fehlt. Wenn Sie es nicht erkennen können, ist ein Decoder, der beide Alphabete akzeptiert, die sichere Wahl.

So bestimmen Sie schnell eine Zeichenkette, die Sie bekommen haben:

  • Sie enthält - oder _: Base64URL.
  • Sie enthält + oder /: Standard-Base64.
  • Ihre Länge ist kein Vielfaches von 4, und sie hat kein =: ohne Padding, was fast immer Base64URL bedeutet.
  • Sie enthält sowohl + als auch - oder sowohl / als auch _: Irgendetwas hat die Alphabete vermischt, oder die Daten sind gar kein Base64. Der Base64-Validator zeigt, welche Zeichen fehl am Platz sind.
  • Sie hat drei durch Punkte getrennte Teile, die mit eyJ beginnen: ein JWT, und das ist per Definition Base64URL.

Warum es Base64URL gibt

In einer URL trennt / Pfadsegmente, + bedeutet in formularkodierten Query-Strings ein Leerzeichen, und = trennt Schlüssel von Werten. Standard-Base64 verwendet alle drei. Um es in eine URL zu setzen, muss man es prozentkodieren, aus ab+/8= wird also ab%2B%2F8%3D: länger, hässlicher und ein weiterer Schritt, bei dem etwas schiefgehen kann.

RFC 4648 §5 behebt das mit - und _, die in URLs nicht reserviert und auf allen gängigen Dateisystemen in Dateinamen erlaubt sind. Das RFC merkt außerdem an, dass das Padding entfallen darf, wenn die Länge bekannt ist, da = selbst als %3D maskiert werden müsste. Mehr dazu in der Anleitung zum Padding.

Wo welche Variante verwendet wird

Standard-Base64:

  • MIME-Anhänge und kodierte Header in E-Mails (Base64 in E-Mails)
  • PEM-Zertifikate und -Schlüssel
  • data:-URIs in HTML und CSS
  • HTTP-Basic-Auth-Header
  • Kubernetes Secrets und die meisten JSON-APIs, die Dateien transportieren

Base64URL, meist ohne Padding:

  • JSON Web Tokens: Header, Payload und Signatur sind alle Base64URL ohne Padding (Base64 in JWT)
  • JWK-Schlüssel, JWS und JWE allgemein (RFC 7515–7518)
  • Credential-IDs und Challenges in WebAuthn
  • code_challenge-Werte von OAuth PKCE (RFC 7636)
  • Zufällige URL-Tokens wie Pythons secrets.token_urlsafe(), das für 32 Zufallsbytes eine Zeichenkette mit 43 Zeichen geliefert hat

Zwischen beiden umwandeln

Die Umwandlung ist reine Zeichenersetzung, das Padding wird gesondert behandelt. Die Bytes ändern sich nie.

Standard zu URL-sicher: + durch - ersetzen, / durch _, und = am Ende entfernen. URL-sicher zu Standard: - durch + ersetzen, _ durch /, und = anhängen, bis die Länge ein Vielfaches von 4 ist.

const toUrl = (b64) =>
  b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

const fromUrl = (u) => {
  const b64 = u.replace(/-/g, "+").replace(/_/g, "/");
  return b64 + "=".repeat((4 - (b64.length % 4)) % 4);
};

toUrl("PDw/Pz8+Pg==");   // "PDw_Pz8-Pg"
fromUrl("PDw_Pz8-Pg");   // "PDw/Pz8+Pg=="

Die meisten Sprachen bringen auch einen direkten Encoder mit, die Zeichenjonglage ist also selten nötig:

SpracheBase64URL-EncoderPadding
Pythonbase64.urlsafe_b64encode(b)bleibt; mit .rstrip(b"=") entfernen
Node.jsbuf.toString("base64url")weggelassen
Gobase64.RawURLEncoding / base64.URLEncodingweggelassen / bleibt
JavaBase64.getUrlEncoder().withoutPadding()weggelassen
PHPrtrim(strtr(base64_encode($s), '+/', '-_'), '=')weggelassen

Beim Dekodieren akzeptiert Nodes Buffer.from(s, "base64") beide Alphabete und fehlendes Padding. Pythons base64.urlsafe_b64decode braucht vorher das wiederhergestellte Padding. atob() im Browser lehnt - und _ mit einem InvalidCharacterError ab, wandeln Sie also vorher um oder nutzen Sie, wo verfügbar, Uint8Array.fromBase64 mit alphabet: "base64url". Code für andere Sprachen steht auf den Code-Seiten, zum Beispiel für Python und Java.

Wenn Sie nur eine einzelne Zeichenkette umstellen müssen, erledigen das Base64 in Base64URL und Base64URL in Base64 im Browser.

Fallstricke

+ wird zum Leerzeichen

Das ist der häufigste Fehler. Standard-Base64, das ohne Maskierung in einem Query-String landet, wird von der Formulardekodierung verstümmelt, die + als Leerzeichen liest:

from urllib.parse import parse_qs
parse_qs("t=ab+/8=")
# {'t': ['ab /8=']}
new URLSearchParams("t=ab+/8=").get("t");
// "ab /8="

Der nachgelagerte Decoder scheitert dann am Leerzeichen oder überspringt es und liefert falsche Bytes. Abhilfe schafft entweder Base64URL oder encodeURIComponent() auf dem Hinweg. Als schnelle Reparatur auf der Empfängerseite funktioniert es meist, Leerzeichen wieder durch + zu ersetzen, denn Base64 enthält nie Leerzeichen.

Stillschweigend vermischte Alphabete

Pythons base64.b64decode verwirft Zeichen außerhalb des Standardalphabets, solange Sie nicht validate=True übergeben. Bekommt es eine Base64URL-Zeichenkette, lässt es - und _ fallen und dekodiert den Rest:

import base64
base64.b64decode("PDw_Pz8-Pg==")
# b'<<\x0f\xcf\xc3\xe0'   (wrong; the input was "<<???>>")
base64.b64decode("PDw_Pz8-Pg==", validate=True)
# binascii.Error: Only base64 data is allowed
base64.urlsafe_b64decode("PDw_Pz8-Pg==")
# b'<<???>>'

Der erste Aufruf liefert falsche Bytes, ohne einen Fehler auszulösen. Nehmen Sie urlsafe_b64decode für URL-sichere Eingaben oder validate=True, damit die Abweichung auffällt.

Doppelte Kodierung

Unter diesem Namen laufen zwei verschiedene Dinge, und beide kommen in der Praxis vor.

Doppelte Prozentkodierung. Eine Standard-Base64-Zeichenkette wird einmal von Ihrem Code und ein weiteres Mal von einem HTTP-Client maskiert, aus + wird also %2B und dann %252B. Der Server dekodiert einmal und erhält ein wörtliches %2B. Base64URL umgeht das Problem, weil es nichts zu maskieren gibt.

Base64 von Base64. Ein Wert wird auf zwei Ebenen kodiert, etwa von einer Anwendung und noch einmal von einem Framework oder von Tools für Kubernetes-Manifeste. Aus Man wird TWFu, dann VFdGdQ==. Das Erkennungszeichen: Das Dekodieren liefert etwas, das immer noch aus Base64-Zeichen besteht. Dekodieren Sie erneut und prüfen Sie, ob das Ergebnis Sinn ergibt.

Erwartungen ans Padding

Manche Empfänger verlangen Padding, andere lehnen es ab. Gos RawURLEncoding scheitert an Eingaben, die = enthalten, Pythons Decoder an Eingaben, denen es fehlt. Wenn Sie eine API definieren, nennen Sie Variante und Padding-Regel in der Dokumentation und lassen Sie Ihren eigenen Decoder beides akzeptieren.

Base64URL dekodieren

Fügen Sie eine URL-sichere Zeichenkette ein, mit oder ohne Padding, und sehen Sie, was darin steckt.

Base64URL-Decoder

Häufige Fragen

Ist Base64URL dasselbe wie eine URL-kodierte Base64-Zeichenkette?

Nein. Die Prozentkodierung macht aus + ein %2B und aus / ein %2F, wodurch die Zeichenkette länger wird. Base64URL verwendet ein anderes Alphabet, sodass gar keine Maskierung nötig ist.

Kann ein Standard-Base64-Decoder Base64URL lesen?

Nicht zuverlässig. Manche Decoder, etwa Buffer in Node, akzeptieren beide Alphabete. Andere lehnen - und _ ab, und Pythons b64decode verwirft sie standardmäßig stillschweigend, was falsche Bytes ergibt.

Hat Base64URL Padding?

RFC 4648 erlaubt es, und Pythons urlsafe_b64encode behält es bei. Die meisten Formate, die Base64URL verwenden, darunter JWT, lassen das = weg, weil die Länge es ohnehin festlegt.

Welche Variante sollte ich für Tokens in URLs verwenden?

Base64URL ohne Padding. Es braucht in Pfaden, Query-Strings, Cookies und Dateinamen keine Maskierung.

Weiterlesen