FlowingDev

Le percent-encoding, expliqué : comprendre %20, %3F et autres hiéroglyphes des URL

Apprenez pourquoi les URL ne peuvent pas contenir certains caractères et comment le percent-encoding traduit les caractères non sécurisés en un format sûr et universel pour le web.

Essayer l'outil: Encodeur d'URL

En une phrase

L'encodage d'URL, officiellement appelé percent-encoding (ou encodage en pourcentage), est le processus qui consiste à traduire les caractères ayant une signification spéciale ou étant invalides dans une URL en un format sûr et universellement compris, afin qu'ils puissent être transmis sans créer de confusion.

Le problème que ça résout

Dans la soupe primordiale du web naissant, la vie était simple. Les URL — ou plus largement, les URI (Uniform Resource Identifiers) — ont été conçues pour être un moyen propre et prévisible de localiser une ressource. Leurs architectes, dont Sir Tim Berners-Lee, ont bâti ce système sur les fondations d'un jeu de caractères limité : l'ASCII.

Ça marchait nickel, tant que vous aviez uniquement besoin de pointer vers http://example.com/rapports/Avril.html. Mais que se passe-t-il quand les choses se compliquent ?

Considérez l'anatomie d'une URL. Elle a plusieurs parties : un schéma (http:), un hôte (example.com), un chemin (/search), et peut-être une chaîne de requête (?q=chiens&chats). Certains caractères sont les metteurs en scène structurels de cette pièce de théâtre. Le deux-points (:) sépare le schéma. Le slash (/) sépare les segments du chemin. Le point d'interrogation (?) lance les paramètres de la requête. L'esperluette (&) sépare un paramètre du suivant.

C'est là que les ennuis commencent. Et si vous vouliez rechercher la chaîne de caractères littérale "C++ & C#" ? Si vous balancez ça tel quel dans une URL, vous obtenez .../search?q=C++ & C#. Un serveur web voit ça et devient complètement paumé. Il pense que la requête est pour "C++ ", puis il voit une esperluette et s'attend à une autre paire clé-valeur, mais il ne reçoit qu'un pauvre " C#" tout seul. Le chaos. L'intention initiale est perdue.

De plus, certains caractères ne sont tout simplement pas autorisés. L'espace est un fauteur de troubles classique. Quand un espace fait-il partie d'un nom de fichier, et quand est-ce juste une faute de frappe qu'un navigateur devrait ignorer ? Et qu'en est-il des caractères en dehors de l'alphabet anglais de base ? Le web est mondial ! Comment mettez-vous Résumé.pdf ou 你好.html dans une URL conçue pour l'ASCII ?

Le percent-encoding résout toute cette catégorie de problèmes. Il fournit une porte de sortie, une façon de dire : « Hé, M. le Serveur Web, le ou les prochains caractères ne sont pas structurels. Ne les interprète pas. Ce sont des données littérales. » C'est le traducteur universel qui garantit qu'une URL signifie la même chose dans un navigateur au Brésil que sur un serveur à Berlin.

Comment ça marche sous le capot

La « magie » derrière le percent-encoding est étonnamment simple. C'est moins un tour de magie qu'un simple chiffre de substitution que tout le monde a accepté d'utiliser.

La distribution des rôles : caractères réservés vs. non réservés

D'abord, vous devez savoir quels caractères sont cool et lesquels sont problématiques. Ils se répartissent en quelques groupes.

Type de caractère Caractères Quand encoder
Non réservés A-Z a-z 0-9 - _ . ~ Jamais. Ce sont les VIP du monde des URL. Ils sont toujours sûrs.
Réservés : / ? # [ ] @ ! $ & ' ( ) * + , ; = Parfois. Ils ont une signification structurelle spéciale. Si vous voulez les utiliser pour leur signification (comme / dans un chemin), vous n'encodez pas. Si vous voulez les utiliser comme des données littérales (comme un & dans une requête de recherche), vous devez les encoder.
Autres (non sécurisés) (espace), `< > " % { } \ ^` et tous les caractères non-ASCII

Ce qu'il faut retenir, c'est le contexte. Le caractère ? est acceptable s'il est l'unique ? qui sépare le chemin de la chaîne de requête. Mais si vous avez besoin d'un point d'interrogation littéral à l'intérieur de la valeur d'un paramètre de requête, vous devez l'encoder.

Le tour de magie : Pourcentage + Hexadécimal

Le processus d'encodage est une simple danse en trois temps :

  1. Choisissez un caractère que vous devez encoder. Prenons l'esperluette &.
  2. Trouvez sa valeur en octet en utilisant un jeu de caractères standard. Pour le web, ce standard est l'UTF-8. En UTF-8 (et son prédécesseur ASCII), le caractère & est représenté par le nombre décimal 38.
  3. Convertissez ce nombre en hexadécimal à deux chiffres et faites-le précéder d'un symbole de pourcentage (%). Le nombre décimal 38 est 26 en hexadécimal.

Donc, & devient %26.

Essayons-en quelques autres :

  • Un espace est le décimal 32, ce qui donne 20 en hexadécimal. Encodé : %20.
  • Un point d'interrogation (?) est le décimal 63, soit 3F en hexadécimal. Encodé : %3F.
  • Le symbole de pourcentage (%) lui-même est le décimal 37, soit 25 en hexadécimal. Donc pour encoder un % littéral, vous écrivez %25.

Ce système est brillant car le symbole de pourcentage lui-même n'est pas un caractère non réservé, donc un analyseur sait que chaque fois qu'il voit un %, il doit s'attendre à ce que deux chiffres hexadécimaux suivent.

Et pour les caractères non anglais ?

C'est là que l'UTF-8 devient crucial. Un simple caractère ASCII comme A occupe un seul octet. Mais un caractère comme le é français ou le 好 chinois est représenté par plusieurs octets en UTF-8. Le processus d'encodage est le même, simplement répété pour chaque octet.

Prenons é :

  1. En UTF-8, é est représenté par deux octets : C3 et A9 (en hexadécimal).
  2. Encodez chaque octet séparément :
    • C3 devient %C3.
    • A9 devient %A9.
  3. Combinez-les : é devient %C3%A9.

Le processus de décodage est l'exact inverse. Un navigateur ou un serveur voit %C3%A9, récupère les deux octets C3 et A9, les passe dans un décodeur UTF-8, et récupère le magnifique caractère é.

Histoires vécues

La théorie, c'est bien, mais voyons là où la théorie se confronte à la pratique.

Le cas de la recherche qui disparaît

Une développeuse junior, Maya, était en train de créer une fonctionnalité de recherche pour un site de documentation technique. Les utilisateurs pouvaient rechercher des choses comme "C++", "promises & async/await", etc. Elle construisait l'URL de recherche en concaténant simplement des chaînes de caractères : site.com/search?q= + userInput.

Tout est parti en vrille. Une recherche pour promises & async/await générait l'URL .../search?q=promises & async/await. Le serveur, cependant, ne recevait que le terme de recherche "promises ". Le & était interprété comme un séparateur pour un nouveau paramètre, async/await, qui était rejeté car il n'avait pas de clé. Les résultats de sa recherche étaient complètement faux.

La leçon : Maya a appris une règle d'or du développement web : toujours appliquer le percent-encoding à toute donnée dynamique placée dans un composant d'URL. Après avoir commencé à encoder l'entrée utilisateur, l'URL est devenue correctement .../search?q=promises%20%26%20async%2Fawait. Le serveur recevait maintenant la chaîne complète et correcte, et la recherche fonctionnait parfaitement.

L'incident international

Une boutique en ligne a décidé de mettre en avant un nouveau produit d'un partenaire allemand : le "Fußball". L'équipe marketing a créé une URL conviviale pour celui-ci : store.com/products/Fußball. Sur leurs navigateurs modernes au bureau, tout semblait fonctionner.

Mais le jour du lancement a été un désastre. Les tickets de support client ont afflué. Certains utilisateurs obtenaient des erreurs "404 Not Found". D'autres voyaient une URL qui ressemblait à .../products/Fu%C3%9Fball dans leur barre d'adresse, tandis que d'autres voyaient .../products/FuÃball. Le système était un patchwork de composants anciens et nouveaux, et ils ne géraient pas le caractère non-ASCII ß (Eszett) de manière cohérente. Certaines parties ne l'encodaient pas, d'autres l'encodaient en supposant de l'UTF-8, et certains systèmes hérités le décodaient en supposant un jeu de caractères différent, résultant en du mojibake (des caractères corrompus).

La leçon : Compter sur les navigateurs et les serveurs pour "simplement gérer" les caractères non-ASCII dans les URL est la recette pour un manque de cohérence. Encoder de manière proactive et cohérente tous les caractères non réservés en utilisant le standard UTF-8 garantit que vos URL sont robustes et fonctionnent de manière prévisible dans tout l'écosystème web, ancien et nouveau.

Le fiasco du double encodage

Une équipe construisait un système d'authentification unique (SSO). Le flux fonctionnait ainsi : service-a.com redirigeait l'utilisateur vers sso.com/login, en passant sa propre URL en paramètre pour que l'utilisateur puisse être renvoyé après s'être connecté. L'URL de redirection ressemblait à ceci : sso.com/login?redirect_uri=https://service-a.com/dashboard?param=1.

Le développeur sur service-a.com a été malin et a encodé la valeur de redirect_uri, produisant : sso.com/login?redirect_uri=https%3A%2F%2Fservice-a.com%2Fdashboard%3Fparam%3D1.

Cependant, le framework web qu'ils utilisaient avait une couche de middleware qui, "par sécurité", encodait automatiquement toutes les URL des paramètres de requête sortants. Il a vu la chaîne déjà encodée et l'a encodée à nouveau. Le % de %3A a été transformé en %25, donc %3A est devenu %253A. L'URL finale était une bouillie incompréhensible de double encodage. Lorsque l'utilisateur arrivait sur sso.com, celui-ci décodait l'URL une fois et obtenait la chaîne encodée une seule fois, qu'il ne pouvait pas utiliser comme redirection, cassant complètement le flux de connexion.

La leçon : Soyez conscient de l'ensemble de votre chaîne d'outils. Encodez les données au point de création, et assurez-vous qu'aucun autre système en aval ne les ré-encode. Le double encodage est un bug courant qui donne des maux de tête et transforme une URL valide en un déchet inutile.

Erreurs et pièges courants

  • Encoder l'URL entière. Ne faites jamais ça. Si vous appliquez le percent-encoding à https://example.com, vous obtiendrez quelque chose comme https%3A%2F%2Fexample.com. Ce n'est plus une URL valide ; le schéma et l'autorité ne sont plus qu'un fatras de caractères sans signification. Vous devez uniquement encoder les composants individuels qui en ont besoin (comme les valeurs des paramètres de requête ou des segments de chemin spécifiques).
  • Ne pas encoder du tout. Le péché le plus courant. Injecter des données brutes de l'utilisateur ou des données avec des caractères spéciaux directement dans une chaîne d'URL, c'est chercher les failles de sécurité (comme le Cross-Site Scripting) et les fonctionnalités cassées.
  • Oublier le contexte. Le caractère & est acceptable dans le chemin d'une URL, mais c'est un séparateur réservé dans la chaîne de requête. De même pour /. Vous n'avez pas besoin d'encoder les caractères réservés lorsqu'ils sont utilisés pour leur fonction spéciale.
  • Confondre + et %20. Dans le type de contenu application/x-www-form-urlencoded (utilisé par les formulaires HTML), les espaces sont souvent encodés par un + dans la chaîne de requête. Bien que de nombreux serveurs le comprennent, l'encodage en pourcentage officiel pour un espace est %20. Utiliser %20 est sans ambiguïté et fonctionne correctement dans toutes les parties d'une URL, pas seulement dans la chaîne de requête. Dans le doute, tenez-vous-en à %20.
  • Utiliser un jeu de caractères obsolète. Le web fonctionne en UTF-8. Si vous encodez vos données en utilisant un jeu de caractères différent (comme ISO-8859-1), alors un serveur attendant de l'UTF-8 interprétera mal les octets et bousillera vos données. Spécifiez et utilisez toujours l'UTF-8.

Pourquoi vous devriez vous y intéresser

Si vous écrivez du code qui touche à une URL, vous devez comprendre le percent-encoding. Ce n'est pas optionnel. Vous devriez y penser chaque fois que vous :

  • Construisez une URL à partir de variables ou d'entrées utilisateur.
  • Faites une requête API avec des paramètres dans l'URL.
  • Gérez des caractères internationaux dans des noms de fichiers, des profils d'utilisateurs ou du contenu susceptible d'apparaître dans une URL.
  • Analysez une URL côté serveur pour en extraire des données.
  • Écrivez des redirections ou passez des URL en paramètres à d'autres services.

Bref, le percent-encoding est un élément fondamental de la plomberie du web. L'ignorer mène à des logiciels buggés, non sécurisés et peu fiables. Savoir comment il fonctionne est la marque d'un développeur web professionnel.

Pour aller plus loin

  • RFC 3986: La spécification canonique pour l'Uniform Resource Identifier (URI). La section 2 définit le jeu de caractères et les règles de percent-encoding. C'est la source de vérité ultime.
  • MDN Web Docs: encodeURIComponent(): Un guide pratique pour les développeurs JavaScript, expliquant quelle fonction utiliser et pourquoi. La section "Voir aussi" renvoie à d'autres fonctions d'encodage associées.
  • Wikipedia: Percent-encoding: Un aperçu complet et très lisible du concept, de son histoire et de ses diverses nuances.
  • W3C: Character encodings: Une introduction de haut niveau sur l'importance des encodages de caractères sur le web, avec l'UTF-8 comme héros de l'histoire.

Théorie bouclée. Place à la pratique — 100 % dans ton navigateur.

Essayer l'outil: Encodeur d'URL