Charger un fichier
  1. base64hub
  2. Apprendre Base64
  3. Base64 ou Base64URL
Guide

Base64 ou Base64URL : alphabets, remplissage, pièges

Base64URL, c’est Base64 avec deux caractères changés pour que la sortie puisse aller telle quelle dans une URL ou un nom de fichier. Les octets sont identiques ; les alphabets diffèrent à deux endroits.

La différence en un tableau

Les deux variantes sont définies dans la RFC 4648. La section 4 décrit le Base64 standard, la section 5 l’alphabet « URL and Filename safe ». Elles partagent 62 caractères et le même algorithme ; seuls les index 62 et 63 diffèrent.

Base64 standard (RFC 4648 §4)Base64URL (RFC 4648 §5)
Index 0–61A–Z a–z 0–9identique
Index 62+-
Index 63/_
Remplissage= obligatoire par défautgénéralement omis
Sûr dans un chemin ou une query stringnonoui
Sûr comme nom de fichiernon (/ sépare les chemins)oui

Voici la même entrée encodée des deux façons :

EntréeStandardBase64URLBase64URL sans remplissage
<<???>>PDw/Pz8+Pg==PDw_Pz8-Pg==PDw_Pz8-Pg
octets fb ff+/8=-_8=-_8
ManTWFuTWFuTWFu

La dernière ligne rappelle que beaucoup de chaînes sont identiques dans les deux variantes. On ne peut les distinguer que si elles contiennent +, /, - ou _, ou s’il manque le remplissage. Dans le doute, un décodeur qui accepte les deux alphabets est le choix sûr.

Pour identifier rapidement une chaîne qu’on vous a transmise :

  • Elle contient - ou _ : Base64URL.
  • Elle contient + ou / : Base64 standard.
  • Sa longueur n’est pas un multiple de 4 et elle n’a pas de = : sans remplissage, ce qui signifie presque toujours Base64URL.
  • Elle contient à la fois + et -, ou à la fois / et _ : quelque chose a mélangé les alphabets, ou ce n’est pas du Base64 du tout. Le validateur Base64 montre quels caractères ne sont pas à leur place.
  • Elle a trois parties séparées par des points et commence par eyJ : c’est un JWT, donc du Base64URL par définition.

Pourquoi Base64URL existe

Dans une URL, / sépare les segments du chemin, + désigne une espace dans les query strings encodées comme des formulaires, et = sépare les clés des valeurs. Le Base64 standard utilise les trois. Le mettre dans une URL oblige à l’encoder en pourcent : ab+/8= devient ab%2B%2F8%3D, plus long, moins lisible, et c’est une étape de plus où se tromper.

La RFC 4648 §5 règle le problème en choisissant - et _, qui sont non réservés dans les URL et autorisés dans les noms de fichiers de tous les systèmes de fichiers courants. La RFC note aussi que le remplissage peut être omis quand la longueur est connue, puisque = devrait lui-même être échappé en %3D. Plus de détails dans le guide du remplissage.

Où chaque variante est utilisée

Base64 standard :

  • Pièces jointes et en-têtes encodés des e-mails MIME (Base64 dans l’e-mail)
  • Certificats et clés PEM
  • URI data: en HTML et en CSS
  • En-têtes HTTP Basic Auth
  • Secrets Kubernetes et la plupart des API JSON qui transportent des fichiers

Base64URL, généralement sans remplissage :

  • JSON Web Tokens : l’en-tête, le payload et la signature sont tous en Base64URL sans remplissage (Base64 dans les JWT)
  • Clés JWK, JWS et JWE en général (RFC 7515–7518)
  • Identifiants de credentials et challenges WebAuthn
  • Valeurs code_challenge d’OAuth PKCE (RFC 7636)
  • Jetons d’URL aléatoires, comme secrets.token_urlsafe() de Python, qui a renvoyé une chaîne de 43 caractères pour 32 octets aléatoires

Convertir de l’un à l’autre

La conversion est une simple substitution de caractères, le remplissage étant traité à part. Les octets ne changent jamais.

Du standard vers URL-safe : remplacez + par -, / par _, et supprimez les = finaux. D’URL-safe vers le standard : remplacez - par +, _ par /, et ajoutez des = jusqu’à ce que la longueur soit un multiple de 4.

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=="

La plupart des langages fournissent aussi un encodeur dédié, si bien que ces manipulations de chaînes sont rarement nécessaires :

LangageEncodeur Base64URLRemplissage
Pythonbase64.urlsafe_b64encode(b)conservé ; à retirer avec .rstrip(b"=")
Node.jsbuf.toString("base64url")omis
Gobase64.RawURLEncoding / base64.URLEncodingomis / conservé
JavaBase64.getUrlEncoder().withoutPadding()omis
PHPrtrim(strtr(base64_encode($s), '+/', '-_'), '=')omis

Pour le décodage, Buffer.from(s, "base64") de Node accepte les deux alphabets et l’absence de remplissage. base64.urlsafe_b64decode de Python exige qu’on rétablisse d’abord le remplissage. atob() dans le navigateur rejette - et _ avec une InvalidCharacterError : convertissez d’abord, ou utilisez Uint8Array.fromBase64 avec alphabet: "base64url" là où il est disponible. Le code pour les autres langages se trouve sur les pages de code, par exemple Python et Java.

S’il s’agit juste de convertir une chaîne, Base64 en Base64URL et Base64URL en Base64 le font dans le navigateur.

Pièges

Le + devient une espace

C’est le bug le plus fréquent. Du Base64 standard placé dans une query string sans échappement est abîmé par le décodage de formulaire, qui lit + comme une espace :

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

Le décodeur en aval échoue alors sur l’espace, ou l’ignore et renvoie de mauvais octets. La solution : Base64URL, ou encodeURIComponent() à l’envoi. Pour réparer vite côté réception, remplacer les espaces par des + fonctionne en général, puisque Base64 ne contient jamais d’espaces.

Mélange silencieux des alphabets

base64.b64decode de Python supprime les caractères hors de l’alphabet standard, sauf si vous passez validate=True. Avec une chaîne Base64URL, il laisse tomber les - et _ et décode le reste :

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'<<???>>'

Le premier appel renvoie de mauvais octets sans lever d’erreur. Utilisez urlsafe_b64decode pour une entrée URL-safe, ou validate=True pour que l’incohérence se voie.

Double encodage

Ce nom désigne deux choses différentes, et les deux arrivent en pratique.

Encodage pourcent appliqué deux fois. Une chaîne Base64 standard est échappée une fois par votre code et une seconde fois par un client HTTP : + devient %2B, puis %252B. Le serveur décode une seule fois et reçoit un %2B littéral. Base64URL évite le problème, puisqu’il n’y a rien à échapper.

Base64 de Base64. Une valeur est encodée à deux niveaux, par exemple par une application puis par un framework ou un outil de manifestes Kubernetes. Man devient TWFu, puis VFdGdQ==. Le signe qui ne trompe pas : le décodage donne quelque chose qui est encore composé de caractères Base64. Décodez à nouveau et vérifiez si le résultat a du sens.

Attentes sur le remplissage

Certains destinataires exigent le remplissage, d’autres le rejettent. RawURLEncoding de Go échoue sur une entrée qui contient =, tandis que le décodeur de Python échoue sur une entrée qui n’en a pas. Quand vous définissez une API, indiquez la variante et la règle de remplissage dans la documentation, et faites en sorte que votre propre décodeur accepte les deux.

Décoder du Base64URL

Collez une chaîne URL-safe, avec ou sans remplissage, et voyez ce qu'elle contient.

Décodeur Base64URL

FAQ

Base64URL, est-ce la même chose qu'encoder en URL une chaîne Base64 ?

Non. L’encodage pourcent transforme + en %2B et / en %2F, ce qui allonge la chaîne. Base64URL utilise un autre alphabet : aucun échappement n’est nécessaire.

Un décodeur Base64 standard peut-il lire du Base64URL ?

Pas de façon fiable. Certains décodeurs, comme Buffer de Node, acceptent les deux alphabets. D’autres rejettent - et _, et b64decode de Python les supprime en silence par défaut, ce qui donne de mauvais octets.

Base64URL a-t-il un remplissage ?

La RFC 4648 l’autorise, et urlsafe_b64encode de Python le conserve. La plupart des formats qui utilisent Base64URL, dont JWT, suppriment les = parce que la longueur suffit à les déduire.

Lequel utiliser pour des jetons dans des URL ?

Base64URL sans remplissage. Il ne demande aucun échappement dans les chemins, les query strings, les cookies ou les noms de fichiers.

Continuer la lecture