Encodage, échappement et hachage
Base64 n'est pas un cryptage, btoa n'est pas UTF-8, encodeURI n'est pas encodeURIComponent et SHA-256 n'est pas un hachage de mot de passe. Voici ce que chacun d’entre eux fait réellement et les erreurs spécifiques qui découlent de l’hypothèse contraire.
L'encodage n'est pas un cryptage ni une compression.
L'encodage modifie la façon dont les données sont écrites. Le cryptage change qui peut le lire. La compression modifie l'espace nécessaire. Ce sont trois tâches différentes, et base64 ne fait que la première – ce qui est mauvais, si vous espériez l'une ou l'autre des deux autres.
Base64 prend trois octets à la fois et les réécrit sous forme de quatre caractères tirés d'un alphabet de symboles 64. Quatre caractères portant trois octets signifient que la sortie est toujours environ 33 % plus grande que l'entrée, plus le remplissage. Il existe parce qu'une grande partie de l'infrastructure (en-têtes de courrier électronique, en-têtes HTTP, valeurs de chaîne JSON, URL, attributs XML) a été conçue pour le texte et modifie ou rejette des octets arbitraires. Base64 est l'adaptateur qui vous permet de transmettre des octets via un canal en forme de texte.
N’importe qui peut l’inverser instantanément, sans clé, car il n’y a pas de clé. Si vous base64 un mot de passe, vous avez publié le mot de passe dans un format légèrement gênant. C'est important car la sortie base64 semble brouillée à l'œil humain, ce qui est exactement la propriété qui fait que les gens lui font confiance pour des choses qu'il ne peut pas faire.
Pourquoi btoa() se brise et les deux façons différentes de le faire
Le navigateur vous donne btoa() et atob(), et ils sont plus anciens que les API de texte modernes. btoa est défini sur des « chaînes binaires » : des chaînes dans lesquelles chaque unité de code est un seul octet, de 0 à 255. Le texte n’est pas ça.
Le premier échec est bruyant. Appelez btoa("世界") et vous obtenez une InvalidCharacterError, car U+4E16 ne rentre pas dans un octet. Les pannes bruyantes sont les bonnes : vous les remarquez immédiatement et cherchez une solution.
Le deuxième échec est silencieux, et c’est celui qui arrive en production. Le caractère é est U+00E9, qui tient dans un octet. Alors btoa("café") revient joyeusement, codant é comme l'octet unique 0xE9. Mais é dans UTF-8 fait deux octets, 0xC3 0xA9. La base64 que vous venez de produire décode, dans tous les autres systèmes sur terre, quelque chose qui n'est pas votre texte. Vous saurez des semaines plus tard quand un nom dans une base de données est devenu un caractère de remplacement.
Le correctif consiste à arrêter de traiter le texte comme des octets et à le convertir explicitement. TextEncoder produit le UTF-8 bytes ; codez-les. TextDecoder transforme les octets en texte, et le construire avec { fatal: true } le fait lancer des séquences invalides au lieu de remplacer discrètement U+FFFD, donc un décodage qui ne peut pas être correct échoue plutôt que de renvoyer des absurdités apparemment plausibles. C'est le pipeline utilisé par cette boîte à outils, c'est pourquoi les emoji, combinant des marques et des scripts de droite à gauche, font tous un aller-retour exactement.
- Convertissez le texte en octets avec TextEncoder – n’indexez jamais dans la chaîne.
- Encodez les octets en base64.
- Pour inverser : décodez le base64 en octets, puis décodez les octets en UTF-8 avec fatal : true.
- Si l’étape UTF-8 échoue, la charge utile est binaire et non textuelle. Montrez-le sous forme de sortilège plutôt que de faire semblant.
base64 contre base64url et la question du remplissage
La base64 standard utilise + et / comme ses deux derniers symboles. Les deux sont significatifs dans les URL : + peut être lu comme un espace codé dans les chaînes de requête et / est un séparateur de chemin. Ainsi, la RFC 4648 définit un deuxième alphabet, base64url, qui remplace - et _ à la place. Les JWT l'utilisent, tout comme la plupart des formats de jetons et de nombreuses API.
Le remplissage est l'autre variable. Pads base64 standard avec = donc la longueur de sortie est toujours un multiple de quatre. base64url supprime généralement le remplissage, car = est lui-même un caractère gênant dans une URL et la longueur peut être récupérée arithmétiquement. Un décodeur qui insiste sur le remplissage rejettera les segments JWT parfaitement valides.
Conseil pratique : votre décodeur doit accepter les deux alphabets et tolérer les remplissages manquants, car vous contrôlez rarement ce qu'on vous donne. Votre encodeur doit être explicite sur ce qu'il émet, car le destinataire s'en soucie probablement. L'utilitaire base64 fait ici exactement cela : il accepte tout ce qui est raisonnable et vous permet de choisir précisément ce qu'il produit.
encodeURI et encodeURIComponent : la différence en une phrase
Les deux encodent en pourcentage en utilisant UTF-8. Ils diffèrent uniquement par les caractères qu'ils laissent seuls, et cette différence est toute l'histoire : encodeURIComponent échappe aux délimiteurs réservés, pas encodeURI.
Les délimiteurs réservés sont les caractères qui donnent à une URL sa structure : : /? # [ ] @ ! $ & ' ( ) * + , ; =. encodeURI suppose que vous lui avez transmis une URL qui est déjà correctement structurée et doit le rester, il les préserve donc - il ne transformera pas https:// en https%3A%2F%2F. encodeURIComponent suppose que vous lui avez remis une pièce qui va être déposée dans un emplacement, il y échappe donc, garantissant que la pièce ne peut pas sortir de son emplacement.
Le bug que cela produit est complètement mécanique. Prenez une valeur de recherche de a&b=c. Encodez-le avec encodeURI et ajoutez-le comme ?q=a&b=c, et vous avez créé silencieusement deux paramètres : q est maintenant juste "a", et un b=c parasite est apparu. Encodez-le avec encodeURIComponent et vous obtenez ?q=a%26b%3Dc, un paramètre, valeur correcte. La même classe de bugs permet à une valeur spécialement conçue d'injecter des paramètres dans une URL créée par votre code - c'est pourquoi "utiliser le formulaire du composant pour les valeurs" est une règle de sécurité, pas seulement une règle d'exactitude.
L'encodage des formulaires est une troisième règle qui ressemble à la seconde. application/x-www-form-urlencoded écrit un espace sous la forme + plutôt que %20. Si vous décodez le corps d'un formulaire avec decodeURIComponent simple, chaque signe plus dans les données devient un espace. Chaque champ de recherche qui a déjà transformé "C++" en "C" est ce bug.
Entités HTML et pourquoi les décoder avec innerHTML est une mauvaise habitude
L'échappement pour HTML est étroit et bien compris : & devient &, < devient <, > devient >, et les valeurs d'attribut intérieures " et ' doivent également être échappées. Cinq caractères. Échapper plus que cela - transformer chaque lettre accentuée en une entité nommée - était une solution de contournement à l'époque des encodages de caractères incertains, et est maintenant un style facultatif plutôt que de sécurité.
Le décodage est le lieu où réside la mauvaise habitude. L’astuce d’une ligne qui apparaît dans chaque réponse consiste à attribuer la chaîne au innerHTML d’un élément détaché et à relire son textContent. Cela fonctionne et c'est une mauvaise idée. Vous avez transmis une entrée non fiable à l'analyseur HTML, qui crée de véritables nœuds DOM à partir de celle-ci. Un <img src=x onerror=...> dans cette chaîne devient un élément d'image réel avec un gestionnaire d'erreur réel attaché ; si ce sous-arbre est inséré dans le document, il s'exécute. Il détruit également silencieusement vos données : les balises de l'entrée disparaissent au lieu d'un aller-retour, car l'analyseur les a interprétées comme du balisage plutôt que du texte.
Le décodage correct des entités ne nécessite aucun analyseur : faites correspondre la référence, recherchez le nom dans un tableau ou effectuez l'arithmétique pour une référence numérique. Cela fait quelques dizaines de lignes, il ne peut rien exécuter et il fait fidèlement l'aller-retour. Cette boîte à outils le fait de cette façon, c'est pourquoi le fait de coller une balise de script dans le décodeur d'entité vous montre une balise de script.
Choisir un hachage et les trois questions qui en décident
Un hachage cryptographique transforme n'importe quelle entrée en un résumé de longueur fixe, de sorte qu'il devrait être impossible de trouver deux entrées avec le même résumé. Cette propriété permet à un résumé de remplacer les données – dans une signature, un contrôle d’intégrité ou une adresse de contenu.
Première question : vous protégez-vous contre un accident ou contre un adversaire ? Une somme de contrôle protégeant contre un téléchargement corrompu n'a besoin que d'attraper des retournements aléatoires ; CRC32 va bien. Un résumé dont un attaquant pourrait bénéficier en cas de collision nécessite un hachage toujours debout. Cette distinction explique pourquoi SHA-1 n'est pas simplement "ancien".
SHA-1 est cassé, concrètement. Dans 2017, le travail SHAttered a produit deux fichiers PDF différents avec le même résumé SHA-1. Dans 2020, "SHA-1 is a Shambles" a démontré une collision de préfixes choisis - la variante la plus forte et la plus dangereuse, car elle permet à un attaquant d'entrer en collision avec deux documents significativement différents plutôt que deux blobs soigneusement construits. Si la sécurité d’un système repose sur la résistance aux collisions SHA-1, cette sécurité disparaît. SHA-1 reste dans cette boîte à outils car les identifiants d'objet git et une longue queue de signatures d'API héritées l'utilisent toujours, et vous devez être en mesure de reproduire ces valeurs. Reproduire une valeur n’est pas la même chose que s’appuyer sur elle.
Deuxième question : la saisie est-elle un mot de passe ? Si tel est le cas, aucune de ces réponses n’est la réponse. SHA-256 est conçu pour être rapide, et la rapidité est précisément mauvaise pour les mots de passe : cela signifie qu'un attaquant utilisant votre base de données peut tenter des milliards de suppositions par seconde. Les mots de passe nécessitent une fonction délibérément lente et gourmande en mémoire avec un sel par utilisateur : Argon2id, scrypt ou bcrypt. Ce n’est pas une nuance ; l'utilisation de SHA-256 pour les mots de passe est l'erreur de hachage grave la plus courante.
Troisième question : avez-vous besoin d'un résumé à clé ? Si vous authentifiez un message plutôt que de prendre ses empreintes digitales, vous voulez HMAC, pas un simple hachage. Concaténer un secret et le hacher est un but contre son camp classique contre les attaques par extension de longueur ; HMAC existe parce que cette construction est plus difficile à réaliser qu’il n’y paraît.
Pour tout le reste – empreinte digitale d'un fichier, adresse de contenu, attribut d'intégrité – SHA-256 est la valeur par défaut raisonnable, et SHA-512 est souvent plus rapide sur le matériel 64 bits tout en offrant un résumé plus large.
Pourquoi les hachages ici proviennent du navigateur
Les résumés de cette boîte à outils sont calculés par SubtleCrypto, la propre implémentation Web Crypto du navigateur, et non par le JavaScript expédié depuis ce site. Il s’agit d’un choix délibéré : l’implémentation du navigateur est auditée, maintenue et s’exécute généralement sous forme de code natif optimisé. Un SHA-256 écrit à la main dans un ensemble de pages constitue davantage de code auquel faire confiance sans aucun avantage.
Cela a une conséquence visible. Web Crypto n'est exposé que dans un contexte sécurisé, c'est-à-dire https:// ou localhost. Ouvrez cette page via HTTP simple sur une adresse LAN et crypto.subtle ne sera pas défini, donc l'utilitaire de hachage vous le dira clairement plutôt que d'échouer silencieusement ou de remplacer quelque chose de plus faible.
Le même raisonnement anime le générateur UUID. crypto.randomUUID() est également sécurisé uniquement en contexte, donc lorsqu'il n'est pas disponible, la boîte à outils revient à crypto.getRandomValues() — qui est toujours la même source cryptographiquement sécurisée — et définit elle-même la version et les bits de variante. Ce qu'il ne fera jamais, c'est revenir à Math.random(). Il s'agit d'un PRNG non cryptographique rapide dont l'état interne peut être récupéré à partir d'une courte série de ses sorties, et les identifiants ont la malheureuse habitude d'être promus en clés de session et en liens de réinitialisation de mot de passe. Si aucune source sécurisée n'existe, cet outil ne génère rien et indique pourquoi.
Qu'arrive-t-il à ce que vous collez
- Chaque conversion, hachage, décodage et différence s'exécute dans l'onglet de votre navigateur. Aucune entrée n'est téléchargée, enregistrée ou stockée sur un serveur, car aucun serveur n'est impliqué une fois la page chargée.
- Les hachages proviennent de la propre implémentation Web Crypto du navigateur et les UUID de son générateur aléatoire cryptographiquement sécurisé. Ni l’un ni l’autre n’implique un appel réseau.
- Rien de ce que vous tapez n’est écrit dans le stockage local ou dans un cookie. Le rechargement de la page la supprime ; la fermeture de l'onglet le supprime.
- Les analyses à l'échelle du site s'exécutent uniquement sur l'hôte de production canonique configuré et sont divulguées dans la politique de confidentialité ; les hôtes locaux et de prévisualisation le refusent. Les valeurs collées, les jetons, les URL et le contenu des fichiers sont exclus des propres événements d'analyse de ToolAcre. La publicité est désactivée dans la configuration actuelle.
- Cela dit : un JWT ou une clé API est un identifiant en direct. La bonne habitude est de ne jamais en coller un dans une page Web que vous n’avez pas écrite, aussi dignes de confiance que ses affirmations – y compris celle-ci.
Questions
Base64 est-il un moyen de masquer des données ?
Non. Il s’agit d’une représentation textuelle réversible, sans clé, décodable par n’importe qui en une fraction de seconde. Cela permet aux données de survivre aux canaux contenant uniquement du texte ; cela ne le rend pas secret. Tout ce qui est véritablement sensible nécessite un chiffrement, et le résultat chiffré est souvent ensuite codé en base64 pour le transport, ce qui est la source de la confusion.
Pourquoi ma base64 est-elle plus longue que l'entrée ?
Étant donné que quatre caractères de sortie contiennent trois octets d'entrée, la sortie a donc environ 4/3 la taille, plus jusqu'à deux caractères de remplissage. C’est inhérent au format. Si la taille est importante, compressez avant l’encodage – jamais après, car la sortie base64 se compresse mal.
Quelle fonction d'encodage d'URL dois-je utiliser ?
Utilisez encodeURIComponent pour tout élément que vous insérez dans une URL : une valeur de requête, un segment de chemin, un fragment. Utilisez encodeURI uniquement lorsque vous disposez d'une URL complète déjà structurée qui contient simplement des espaces ou non-ASCII. Si vous créez une chaîne de requête, préférez URLSearchParams, qui applique la règle correcte pour vous et gère la différence d'espace en plus.
Pourquoi mon décodeur renvoie-t-il « URI mal formé » ?
Parce qu'un % dans l'entrée n'est pas suivi de deux chiffres hexadécimaux. Habituellement, le texte contient un signe de pourcentage littéral — « 50% off » — qui n'a jamais été codé. Un pourcentage littéral doit être écrit sous la forme %25. L'utilitaire URL rapporte ici la position exacte de l'échappement incriminé au lieu de simplement refuser.
Puis-je utiliser SHA-256 pour stocker des mots de passe ?
Non. SHA-256 est rapide de par sa conception, ce qui signifie qu'un attaquant qui vole votre base de données peut tester des milliards de mots de passe candidats par seconde sur du matériel standard. Les mots de passe nécessitent une fonction salée, lente et gourmande en mémoire : Argon2id, scrypt ou bcrypt. C’est l’erreur grave la plus courante dans ce domaine.
Pourquoi SHA-1 est-il toujours là s'il est cassé ?
Parce que vous devez toujours reproduire les valeurs SHA-1 qui existent déjà : identifiants d'objet git, anciennes empreintes digitales de certificat TLS, signatures de requêtes d'API héritées. Être capable de calculer une valeur d’interopérabilité n’est pas la même chose que de s’y fier pour la sécurité. Chaque endroit où SHA-1 apparaît dans cette boîte à outils est étiqueté en conséquence.
Pourquoi deux outils donnent-ils des hachages différents pour le même texte ?
Presque toujours une différence dans les octets, pas dans l'algorithme. Les coupables habituels sont une nouvelle ligne de fin (un fichier se termine par un ; une zone de texte peut ne pas l'être), un encodage de texte différent ou des fins de ligne CRLF versus LF. Cet outil hache le UTF-8 bytes de exactement ce que vous avez tapé et vous montre le nombre d'octets, ce qui rend généralement l'écart évident.
Limites
- Le tableau des entités HTML nommées couvre le sous-ensemble pratique : caractères critiques pour le balisage, typographie, devise, flèches, mathématiques, grec et latin-1 - pas toutes les 2,231 références nommées HTML5. Les noms non reconnus sont signalés et laissés exactement tels qu'ils sont écrits plutôt que devinés.
- Le décodage d’entité nécessite le point-virgule final. HTML5 tolère une poignée de références héritées sans aucune, mais leur décodage correct dépend du contexte de balisage environnant, ce qu'un outil de texte autonome ne possède pas.
- Le hachage et la génération d'UUID nécessitent un contexte sécurisé (https:// ou localhost) car Web Crypto n'est pas exposé autrement. L'outil le signale plutôt que de remplacer une implémentation plus faible.
- Seuls SHA-1, SHA-256, SHA-384 et SHA-512 sont disponibles, car c'est ce que SubtleCrypto implémente. MD5 est absent par choix ainsi que par nécessité.
- Il n'y a pas de HMAC, pas de dérivation de clé et pas de cryptage ici. Ceux-ci nécessitent une gestion des clés, ce qui n’est pas quelque chose qu’une page que vous avez trouvée sur Internet devrait gérer.
- Tout est limité par la mémoire de votre appareil, puisque tout s'exécute dans un seul onglet de navigateur. Les entrées sont plafonnées – quelques mégaoctets par utilitaire – et l’outil refuse les travaux surdimensionnés plutôt que de les geler.