Skip to content

Obfuscation JavaScript : escape et unescape

Certains sites tentent de dissimuler du code JavaScript sensible en remplaçant ses caractères par des séquences d’échappement via la fonction escape(). Cette méthode, obsolète depuis ES5, ne constitue pas une protection : un appel à unescape() restitue le code original en clair.

escape() encode une chaîne en remplaçant les caractères par leur représentation hexadécimale ou unicode :

escape("if (password == 'secret') { login(); }")
// → "if%20%28password%20%3D%3D%20%27secret%27%29%20%7B%20login%28%29%3B%20%7D"

Le code livré dans la page ressemble alors à une suite de caractères encodés, ce qui donne l’illusion d’une protection.

Dans la pratique, la page doit bien exécuter ce code, donc elle appelle elle-même unescape() ou eval() pour le décoder au moment de l’exécution. Le code décodé transite dans le navigateur — il est accessible.

Copier la chaîne encodée trouvée dans le source de la page et l’exécuter dans la console DevTools :

unescape("if%20%28password%20%3D%3D%20%27secret%27%29%20%7B%20login%28%29%3B%20%7D")
// → "if (password == 'secret') { login(); }"

Si le code obfusqué est passé à eval(), surcharger eval avant le chargement de la page pour lire ce qui lui est transmis :

const _eval = eval;
window.eval = function(code) {
console.log("eval intercepté :", code);
return _eval(code);
};

Recharger la page : le code décodé s’affiche dans la console avant son exécution.

Dans les DevTools, onglet Sources → poser un point d’arrêt sur la ligne contenant eval() ou unescape(). À l’exécution, inspecter la variable qui reçoit le résultat : le code original est lisible dans le panneau Scope.

Ce schéma indique que le développeur a tenté de cacher quelque chose de précis dans ce bloc : des credentials, une logique d’autorisation, une clé API, ou une URL interne. Le code obfusqué mérite une lecture attentive une fois décodé.

Les patterns à chercher après décodage :

// Credentials en dur
if (user == "admin" && pass == "p@ssw0rd") { ... }
// Clé API
var apiKey = "sk-live-abc123...";
// URL interne
fetch("https://internal.api.exemple.com/admin/users")
// Logique de contournement
if (isPremium === true) { unlockContent(); }

escape() et unescape() sont dépréciées depuis ECMAScript 5 (2009). La MDN les classe comme fonctionnalités à ne pas utiliser en production — non pour des raisons de sécurité, mais parce qu’elles gèrent mal les caractères Unicode au-delà de U+00FF.

Leur usage comme mécanisme d’obfuscation repose sur l’hypothèse qu’un attaquant ne connaît pas unescape() — ce qui n’est pas une hypothèse valide.

D’autres méthodes d’obfuscation plus élaborées existent (substitution de variables, encodage en base64 imbriqué, packers comme p,a,c,k,e,d), mais elles restent toutes décodables : le navigateur doit pouvoir exécuter le code, donc le décoder.

L’obfuscation ne remplace pas la sécurité. Les éléments sensibles — credentials, clés, logique d’autorisation — n’ont pas leur place dans le JavaScript livré au client, obfusqué ou non.

  • Déplacer la logique sensible côté serveur : un endpoint API authentifié, pas une condition JavaScript
  • Ne jamais stocker de secrets dans le code front-end : utiliser des variables d’environnement injectées au build, ou mieux, des appels authentifiés vers un backend
  • Si une obfuscation est nécessaire pour des raisons de propriété intellectuelle, utiliser des outils reconnus (terser, javascript-obfuscator) en sachant qu’ils ralentissent la lecture sans empêcher le décodage

Tout ce que le navigateur peut décoder pour l’exécuter, un attaquant peut le décoder pour le lire. L’obfuscation via escape() ajoute trente secondes de travail à quelqu’un qui sait ce qu’il fait.