Charger un fichier
  1. base64hub
  2. Apprendre Base64
  3. Encodage ou chiffrement
Sécurité

Base64 n'est pas du chiffrement : encodage ou chiffrement

Base64 ne change que la façon dont les octets sont écrits. Tout ce que vous encodez en Base64 peut être décodé par quiconque le voit, en une commande et sans clé.

Trois opérations différentes

Base64 apparaît régulièrement là où l’on s’attend à une protection : il vaut donc la peine d’être précis sur ce qu’il fait. C’est un encodage : une correspondance fixe et publique entre des octets et du texte (voir le fonctionnement de Base64). Cette correspondance n’a ni clé ni secret, et la même entrée produit toujours la même sortie.

Encodage (Base64, hex)Chiffrement (AES-GCM)Hachage (SHA-256, Argon2)
ObjectifReprésenter des octets sous forme de texteGarder les données confidentiellesPrendre l’empreinte de données ou les vérifier
RéversibleOui, par n’importe quiOui, avec la cléNon
Nécessite une cléNonOuiNon (HMAC en utilise une)
Même entrée, même sortieToujoursNon, avec un nonce aléatoireOui, sauf avec un sel
Taille de sortieEnviron 4/3 de l’entréeEntrée plus nonce et tagLongueur fixe

Décoder ne demande aucun effort :

echo ZGVwbG95Omh1bnRlcjI= | base64 -d
# deploy:hunter2

Pour une personne qui y jette un œil, cette chaîne paraît opaque. Pour un script, un scanner de logs ou un attaquant, c’est du texte en clair avec une étape de plus.

Un test utile : si une valeur poserait problème sur une capture d’écran, elle pose toujours problème une fois encodée en Base64.

Comment les données en Base64 fuient en pratique

Le schéma est toujours le même : une valeur est encodée en Base64 pour le transport, quelqu’un la prend pour une valeur protégée, puis elle finit quelque part où on peut la lire. Voici les formes courantes de cette erreur.

En-têtes HTTP Basic Auth

L’authentification Basic (RFC 7617) envoie le nom d’utilisateur et le mot de passe sous la forme Base64(user:password) dans chaque requête :

Authorization: Basic ZGVwbG95Omh1bnRlcjI=

La seule protection est le TLS de la connexion. Dès que l’en-tête sort de ce tunnel, le mot de passe est de fait en clair. Les cas typiques : des reverse proxies ou des frameworks applicatifs qui journalisent les en-têtes de requête, des fichiers HAR exportés depuis les outils de développement du navigateur et joints à un ticket de support, une sortie curl -v collée dans une messagerie, des outils de suivi d’erreurs qui capturent les requêtes complètes. Le décodeur Basic Auth montre à quel point il en faut peu pour en lire un.

Secrets Kubernetes

Un Secret Kubernetes stocke ses valeurs encodées en Base64 :

apiVersion: v1
kind: Secret
metadata:
  name: db
data:
  password: czNjcjN0

czNjcjN0, c’est s3cr3t. L’encodage existe pour que les valeurs puissent contenir des données binaires dans du YAML. La documentation de Kubernetes indique que les Secrets sont stockés non chiffrés dans etcd par défaut, et quiconque a le droit de faire un get sur le Secret via l’API peut le lire. Les fuites courantes : des manifestes avec de vraies valeurs data: commités dans un dépôt Git, des sorties kubectl get secret -o yaml collées dans des tickets, et des sauvegardes d’etcd ou de l’état du cluster stockées sans chiffrement. Le décodeur de Secrets Kubernetes décode d’un coup toutes les valeurs d’un Secret, ce qui permet aussi de vérifier vite ce qu’un manifeste expose réellement.

Les identifiants de registres de conteneurs suivent le même schéma. Le champ auth d’un config.json Docker ou d’un Secret .dockerconfigjson vaut Base64(user:password) ; le décodeur dockerconfigjson le lit.

Payloads JWT

Un JWT signé ressemble à un jeton scellé, mais ses deux premières parties ne sont que du JSON en Base64URL :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImVtYWlsIjoiYW5hQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIn0.<signature>

Le payload se décode en {"sub":"42","email":"ana@example.com","role":"admin"}. La signature protège le jeton contre la modification ; elle ne fait rien pour la confidentialité. Chaque extension de navigateur, proxy, fichier de log et script d’analytics qui voit le jeton peut lire son contenu. Les données personnelles, les identifiants internes que vous préférez ne pas exposer et tout ce qui est secret n’ont rien à faire dans le payload d’un JWT. S’ils doivent voyager dans un jeton, utilisez un JWT chiffré (JWE). Plus de détails dans Base64 dans les JWT.

Secrets « obfusqués » dans le code et la configuration

Les clés d’API stockées en Base64 dans du JavaScript front-end, des bundles d’applications mobiles ou des fichiers de configuration sont un classique. Le raisonnement habituel : cela évite que la clé soit « en clair ». Cela évite en effet qu’elle corresponde à une recherche naïve de la clé littérale, et c’est tout. Tout ce qui est livré à un client peut être lu par ce client.

La même idée sert dans l’autre sens lors d’attaques. Le -EncodedCommand de PowerShell prend du Base64 de texte UTF-16LE, et il est souvent utilisé pour rendre des commandes malveillantes plus difficiles à lire d’un coup d’œil. Les défenseurs le décodent en quelques secondes, par exemple avec le décodeur de commandes PowerShell encodées.

Que faire à la place

Choisissez l’outil selon la tâche. Base64 garde sa place dans chacun de ces cas, comme enveloppe texte autour d’octets protégés par autre chose.

Pour garder des données confidentielles : le chiffrement authentifié

Utilisez AES-256-GCM ou ChaCha20-Poly1305, via une bibliothèque bien maintenue. Les deux sont authentifiés : toute altération est détectée au déchiffrement. Deux règles comptent plus que l’algorithme : ne jamais réutiliser un nonce avec la même clé, et garder la clé ailleurs qu’à côté des données (un KMS, un gestionnaire de secrets ou un environnement que l’attaquant ne contrôle pas aussi).

import { randomBytes, createCipheriv, createDecipheriv } from "node:crypto";

const key = randomBytes(32);   // in real code, load this from a KMS or secret store
const iv = randomBytes(12);    // 96-bit nonce, unique per message

const cipher = createCipheriv("aes-256-gcm", key, iv);
const ct = Buffer.concat([cipher.update("deploy:hunter2", "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();

// Base64 carries the encrypted bytes as text: nonce + tag + ciphertext
const token = Buffer.concat([iv, tag, ct]).toString("base64");

const raw = Buffer.from(token, "base64");
const d = createDecipheriv("aes-256-gcm", key, raw.subarray(0, 12));
d.setAuthTag(raw.subarray(12, 28));
console.log(Buffer.concat([d.update(raw.subarray(28)), d.final()]).toString("utf8"));
// deploy:hunter2

La sortie est une chaîne Base64 de 56 caractères qui change à chaque exécution, car le nonce est aléatoire. Sans la clé, elle ne sert à rien. Pour expérimenter sans écrire de code, les outils chiffrement AES et déchiffrement AES fonctionnent dans votre navigateur.

Pour stocker des mots de passe : un hachage lent et salé

Les mots de passe ne doivent être ni chiffrés ni encodés. Stockez un hachage produit par une fonction conçue pour les mots de passe : Argon2id, scrypt ou bcrypt, avec un sel aléatoire unique par mot de passe. Un simple SHA-256 d’un mot de passe se calcule trop vite et cède à la force brute. La bibliothèque standard de Python inclut scrypt :

import hashlib, hmac, os

def hash_password(pw: str) -> bytes:
    salt = os.urandom(16)
    return salt + hashlib.scrypt(pw.encode(), salt=salt, n=2**14, r=8, p=1)

def check_password(pw: str, stored: bytes) -> bool:
    salt, digest = stored[:16], stored[16:]
    candidate = hashlib.scrypt(pw.encode(), salt=salt, n=2**14, r=8, p=1)
    return hmac.compare_digest(candidate, digest)

Si vous devez placer le résultat dans une colonne texte ou du JSON, encodez-le en Base64.

Pour prouver l’intégrité : signatures et HMAC

Quand l’objectif est « personne n’a modifié ceci » plutôt que « personne ne peut lire ceci », utilisez un HMAC avec un secret partagé ou une signature numérique. Le résultat est une suite d’octets, généralement transportée en Base64 ou en hex. Le générateur HMAC affiche la sortie sous les deux formes.

Pour les identifiants en transit et au repos

Utilisez TLS pour chaque connexion qui transporte du Basic Auth ou des jetons, préférez des jetons à courte durée de vie aux mots de passe statiques, retirez les en-têtes Authorization des logs, activez le chiffrement au repos des Secrets Kubernetes et gardez les vraies valeurs secrètes hors de Git.

FAQ

Base64 est-il sécurisé ?

Base64 n’a aucune propriété de sécurité. Il n’y a pas de clé : pour décoder, il suffit de la chaîne elle-même. On peut l’utiliser sans risque comme format de transport pour des données déjà protégées, comme un texte chiffré ou une signature.

Base64 suffit-il pour cacher un mot de passe dans un fichier de configuration ?

Non. Quiconque peut lire le fichier peut décoder le mot de passe avec base64 -d. Restreignez les droits sur le fichier, utilisez un gestionnaire de secrets, ou chiffrez la valeur avec une clé stockée ailleurs.

Pourquoi les Secrets Kubernetes sont-ils encodés en Base64 si ce n'est pas pour la sécurité ?

Pour que les valeurs des Secrets puissent contenir des données binaires quelconques, comme des clés TLS ou des keystores, dans du YAML et du JSON. La protection vient du RBAC, du chiffrement au repos d’etcd et d’une manipulation prudente des manifestes.

Peut-on lire le payload d'un JWT sans le secret ?

Oui. L’en-tête et le payload sont du JSON en Base64URL que tout le monde peut lire. La clé n’est nécessaire que pour vérifier ou créer la signature. Essayez avec le décodeur JWT.

Quelle différence entre encodage, chiffrement et hachage ?

L’encodage est réversible par n’importe qui. Le chiffrement n’est réversible qu’avec une clé. Le hachage est à sens unique : on peut vérifier une valeur par rapport à un hachage, mais pas retrouver la valeur à partir du hachage.

Continuer la lecture