Charger un fichier
  1. base64hub
  2. Apprendre Base64
  3. Remplissage (=)
Explication

Le remplissage Base64 : à quoi servent les signes =

Le = à la fin d’une chaîne Base64 signale un dernier bloc incomplet. Certains formats l’omettent, certains décodeurs l’exigent, et le rétablir tient en une ligne de code.

Pourquoi Base64 a besoin de remplissage

Base64 convertit 3 octets en 4 caractères (l’algorithme complet est dans le fonctionnement de Base64). Quand la longueur de l’entrée n’est pas un multiple de 3, le dernier bloc est incomplet :

Octets dans le dernier blocCaractères de donnéesRemplissageExemple
34aucunMan → TWFu
23=Ma → TWE=
12==M → TQ==

Le = maintient la longueur de sortie à un multiple de 4. Cela simplifiait la vie des décodeurs de l’époque MIME, qui pouvaient lire l’entrée par groupes fixes de 4 caractères et savoir où s’arrêtent les données sans suivre la longueur totale.

À strictement parler, l’information est redondante. Un dernier groupe de 3 caractères ne peut signifier que 2 octets, et un groupe de 2 caractères qu’un seul octet : un décodeur qui compte les caractères n’a donc besoin d’aucun =. C’est pourquoi le Base64 sans remplissage est possible, et pourquoi de nombreux formats l’utilisent.

Il existe un cas où le remplissage porte une vraie information : la concaténation. Si deux chaînes avec remplissage sont collées l’une à l’autre, le = du milieu marque la fin de la première. Les décodeurs gèrent pourtant mal ce cas. Avec TQ==TQ== (deux M encodés), le décodeur par défaut de Python a renvoyé b'M\x04\xd0', Buffer de Node a renvoyé seulement M, et atob a levé une exception. Décodez plutôt chaque morceau séparément.

Quand le remplissage est facultatif

La RFC 4648 §3.2 dit que les encodeurs DOIVENT ajouter le remplissage, sauf si la spécification qui utilise Base64 dit explicitement le contraire. Plusieurs le font :

  • JWT et JOSE (RFC 7515) utilisent Base64URL sans remplissage. Un en-tête JWT comme eyJhbGciOiJIUzI1NiJ9 n’a pas de = même quand sa longueur n’est pas un multiple de 4. Voir Base64 dans les JWT.
  • Les URL et les noms de fichiers transportent souvent du Base64URL sans remplissage, car = a un sens dans les query strings et devrait être échappé en %3D. La page Base64 ou Base64URL détaille toute la différence.
  • WebAuthn et OAuth PKCE (le code_challenge de la RFC 7636) utilisent aussi du Base64URL sans remplissage.

Le Base64 standard des e-mails, des fichiers PEM, des data URI et de la plupart des API JSON a un remplissage. Dans le doute, produisez une sortie avec remplissage pour le Base64 standard et sans remplissage pour le Base64URL, et faites en sorte que votre décodeur accepte les deux.

Les décodeurs qui exigent le remplissage

Les comportements varient plus qu’on ne le croit. Voici les résultats du décodage de TWE (Ma sans remplissage) avec le décodeur par défaut de chaque bibliothèque :

Langage / outilAppelRésultat pour TWE
Python 3.13base64.b64decodebinascii.Error: Incorrect padding
Python 3.13base64.urlsafe_b64decodeIncorrect padding également
.NETConvert.FromBase64StringFormatException
Gobase64.StdEncodingerreur ; utilisez RawStdEncoding pour une entrée sans remplissage
Gobase64.RawStdEncodingMa, mais rejette TWE=
RubyBase64.strict_decode64ArgumentError: invalid base64
RubyBase64.decode64, urlsafe_decode64Ma
Java 21Base64.getDecoder().decodeMa
Node.jsBuffer.from(s, 'base64'), atobMa
PHP 8.4base64_decode (mode strict compris)Ma
GNU coreutils 9.7base64 -dMa
OpenSSL 3.5openssl base64 -dsortie vide, code de sortie 0

Notez la ligne OpenSSL : la commande n’a rien affiché et a quand même signalé un succès : un script qui fait passer du Base64 sans remplissage par openssl base64 -d produit donc en silence un fichier vide. Dans les mêmes tests, elle n’a rien renvoyé non plus pour une ligne unique sans saut de ligne final ; l’option -A a réglé ce cas.

Go est le plus strict dans l’autre sens : RawStdEncoding refuse une chaîne qui a un remplissage. Si vous acceptez des entrées de plusieurs sources, retirez les = et utilisez l’encodage raw, ou ajoutez le remplissage et utilisez l’encodage standard.

Des exemples propres à chaque langage se trouvent sur les pages de code, par exemple Base64 en Python et Base64 en Go.

Comment rétablir un remplissage manquant

La règle ne dépend que de la longueur de la chaîne une fois les espaces retirés :

len % 4À ajouterSignification
0rienblocs complets
2==le dernier bloc contient 1 octet
3=le dernier bloc contient 2 octets
1impossible à corrigertronquée, ou caractère parasite

Un reste de 1 n’est jamais valide. Un caractère Base64 porte 6 bits, ce qui ne suffit pas pour un octet : aucun remplissage ne rendra la chaîne décodable. Si vous rencontrez ce cas, cherchez un caractère manquant, un caractère en trop ou un saut de ligne compté par erreur.

La correction tient en une expression dans la plupart des langages :

import base64
s = "eyJzdWIiOiIxMjM0In0"            # 19 chars, 19 % 4 == 3
padded = s + "=" * (-len(s) % 4)     # adds one "="
print(base64.urlsafe_b64decode(padded))
# b'{"sub":"1234"}'
const s = "eyJzdWIiOiIxMjM0In0";
const padded = s + "=".repeat((4 - (s.length % 4)) % 4);
// "eyJzdWIiOiIxMjM0In0="

Le validateur Base64 indique la longueur et l’état du remplissage d’une chaîne, et Réparer du Base64 ajoute les = manquants, retire les espaces et les guillemets, et convertit les caractères URL-safe en une seule étape.

Les encodages non canoniques

Le remplissage a un cousin plus discret : les bits inutilisés du dernier caractère de données. Quand Ma est encodé, ses 16 bits remplissent deux groupes et demi de 6 bits. Le troisième caractère porte les 4 derniers bits de a plus 2 bits de bourrage, et la RFC 4648 §3.5 dit que l’encodeur doit mettre ces bits de bourrage à zéro. Cela donne E (000100).

Un décodeur ignore les bits de bourrage : TWF= (où F vaut 000101, avec les bits de bourrage 01) se décode donc lui aussi en Ma. Python, Node, Java et PHP ont tous accepté TWF= et renvoyé Ma dans nos tests. De même, TR== se décode en M, exactement comme TQ==.

Les chaînes comme TWF= sont dites non canoniques. Elles posent problème à quelques endroits :

  • Signatures et hachages calculés sur du texte Base64. Deux chaînes différentes se décodent en mêmes octets : comparer des chaînes Base64 pour savoir si des données sont égales peut donc donner une mauvaise réponse. Comparez les octets décodés.
  • Clés de déduplication et de cache. Même problème : normalisez en décodant puis en réencodant.
  • Analyseurs stricts. La RFC 4648 permet aux décodeurs de rejeter des bits de bourrage non nuls. StdEncoding.Strict() de Go et base64 -d de GNU rejettent tous deux TWF=.

Un remplissage en trop est une bizarrerie voisine. TWE== a un = de trop. Python et Buffer de Node l’ont accepté, atob a levé InvalidCharacterError.

Le remplissage lors d’un encodage par morceaux

Si vous encodez un gros fichier en Base64 par morceaux, chaque morceau sauf le dernier doit faire un multiple de 3 octets. Sinon, chaque morceau reçoit son propre remplissage et la sortie assemblée est fausse. Encoder hello world!! par morceaux de 4 octets a donné aGVsbA==byB3bw==cmxkIQ==IQ== ; par morceaux de 3 octets, aGVsbG8gd29ybGQhIQ==, identique à l’encodage du tout en une fois. Pour le découpage des lignes MIME, 57 octets par ligne est la taille de morceau naturelle, puisque 57 octets donnent exactement 76 caractères.

Corriger le remplissage automatiquement

Collez une chaîne au remplissage manquant ou cassé et récupérez du Base64 valide.

Réparation Base64

FAQ

Puis-je simplement supprimer les signes = d'une chaîne Base64 ?

Les données n’en souffrent pas, car la longueur de la chaîne indique déjà au décodeur combien d’octets contient le dernier bloc. Que le résultat se décode encore dépend du destinataire : le Base64URL des JWT s’attend à l’absence de remplissage, tandis que b64decode de Python et Convert.FromBase64String de .NET rejettent une chaîne qui en manque.

Pourquoi ma chaîne Base64 n'a-t-elle pas de = à la fin ?

Soit la longueur de l’entrée était un multiple de 3 octets et aucun remplissage n’était nécessaire, soit le producteur utilise du Base64 sans remplissage, ce qui est courant pour le Base64URL des jetons et des URL.

Comment corriger une erreur Incorrect padding en Python ?

Ajoutez '=' * (-len(s) % 4) avant de décoder. Si la chaîne est URL-safe, utilisez aussi base64.urlsafe_b64decode. Si la longueur mod 4 vaut 1, le remplissage n’y changera rien : la chaîne est tronquée ou contient un caractère parasite.

Une chaîne Base64 peut-elle se terminer par trois signes = ?

Non. Un dernier bloc contient 1 ou 2 octets restants, qui produisent == ou =. Trois = signifieraient un bloc sans données, ce qui n’existe pas.

Continuer la lecture