BLOG

Interception et modification des réponses avec Chrome via le protocole Devtools

Miniature F5
F5
Publié le 17 septembre 2018


Chez Shape, nous rencontrons beaucoup de scripts JavaScript douteux. Nous tombons sur des scripts injectés malicieusement dans des pages, parfois envoyés par un client pour avis, ou bien nos équipes de sécurité détectent sur le web des ressources qui citent précisément certains aspects de notre service. Chaque jour, nous examinons ces scripts de près pour comprendre leur fonctionnement et leurs objectifs. Ils sont généralement minifiés, souvent obfusqués, et demandent plusieurs niveaux de modifications avant d’être prêts pour une analyse approfondie.

Jusqu’à récemment, le moyen le plus simple de réaliser cette analyse consistait soit à utiliser des configurations mises en cache localement qui permettent l’édition manuelle, soit à utiliser des proxys pour réécrire le contenu à la volée. La solution locale est la plus pratique, mais les sites Web ne se traduisent pas toujours parfaitement dans d'autres environnements et cela conduit souvent les gens à se lancer dans un terrier de dépannage juste pour devenir productifs. Les proxys sont extrêmement flexibles, mais sont généralement encombrants et peu portables. Chacun a sa propre configuration personnalisée pour son environnement et certaines personnes sont plus familières avec un proxy qu'avec un autre. J'ai commencé à utiliser Chrome et son protocole devtools afin de me connecter aux requêtes et aux réponses au fur et à mesure qu'elles se produisent et de les modifier à la volée. Il est portable sur n'importe quelle plateforme dotée de Chrome, contourne toute une série de problèmes et s'intègre bien aux outils JavaScript courants. Dans cet article, je vais vous expliquer comment intercepter et modifier JavaScript à la volée à l'aide du protocole devtools de Chrome .

Nous utiliserons Node, mais une grande partie du contenu est portable dans la langue de votre choix, à condition que les hooks devtools soient facilement accessibles.

Tout d’abord, si vous n’avez jamais exploré les scripts Chrome, Eric Bidelman a rédigé un excellent guide de démarrage pour Chrome sans tête . Les conseils qui y sont présentés s'appliquent à la fois à Chrome Headless et GUI (avec une particularité que j'aborderai dans la section suivante).

Lancement de Chrome

Nous utiliserons la bibliothèque chrome-launcher de npm pour faciliter cette tâche.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

chrome-launcher fait exactement ce que vous pensez qu'il ferait et vous pouvez transmettre les mêmes commutateurs de ligne de commande auxquels vous êtes habitué à partir du terminal sans changement (une excellente liste est conservée ici ). Nous allons passer aux options suivantes :

–taille-de-fenêtre=1200,800

  • Définissez automatiquement la taille de la fenêtre sur une valeur raisonnable.

–ouverture automatique des outils de développement pour les onglets

  • Ouvrez automatiquement les outils de développement car nous les utilisons fréquemment.

–user-data-dir=/tmp/chrome-testing

  • Définissez un répertoire de données utilisateur constant. (Idéalement, nous n'en aurions pas besoin, mais le mode non-headless sur Mac OSX ne semble pas vous permettre d'intercepter les requêtes sans cet indicateur. Si vous connaissez une meilleure méthode, faites-le moi savoir via Twitter !)
Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Essayez d’exécuter votre script pour vous assurer que vous pouvez ouvrir Chrome. Vous devriez voir quelque chose comme ceci :

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Utilisation du protocole Chrome Devtools

Ceci est également appelé « protocole de débogage Chrome », et les deux termes semblent être utilisés de manière interchangeable dans les documents de Google. Tout d’abord, installez le package chrome-remote-interface via npm qui nous donne des méthodes pratiques pour interagir avec le protocole devtools. Assurez-vous d'avoir la documentation du protocole à portée de main si vous souhaitez approfondir le sujet.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Pour utiliser le CDP, vous devez vous connecter au port du débogueur et, comme nous utilisons la bibliothèque chrome-launcher , celui-ci est facilement accessible via chrome.port .

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

De nombreux domaines du protocole doivent d’abord être activés, et nous allons commencer par le domaine Runtime afin de pouvoir nous connecter à l’API de la console et transmettre tous les appels de console du navigateur à la ligne de commande.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Désormais, lorsque vous exécutez votre script, vous obtenez une fenêtre Chrome entièrement fonctionnelle qui génère également tous ses messages de console sur votre terminal. C'est génial en soi, surtout à des fins de test !

Interception des demandes

Commencez par enregistrer ce que vous souhaitez intercepter en soumettant une liste de RequestPatterns à setRequestInterception. Vous pouvez intercepter au stade « Request » ou « HeadersReceived ». Pour modifier une réponse, attendez « HeadersReceived ». Le type de ressource correspond aux types que vous retrouvez habituellement dans l’onglet réseau des outils de développement.

N'oubliez pas d'activer le domaine Réseau comme vous l'avez fait avec Runtime , ci-dessus, en ajoutant Network.enable() au même tableau.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

L'enregistrement du gestionnaire d'événements est relativement simple et chaque requête interceptée est accompagnée d'un ​interceptionId qui peut être utilisé pour interroger des informations sur la requête ou éventuellement émettre une continuation. Ici, nous intervenons simplement et enregistrons chaque requête que nous interceptons sur le terminal.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Modification des demandes

Pour modifier les requêtes, nous devrons installer certaines bibliothèques d'aide qui encoderont et décoderont les chaînes base64. Il existe de nombreuses bibliothèques disponibles ; n'hésitez pas à choisir la vôtre. Nous utiliserons atob et btoa .

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

L'API pour gérer les réponses est un peu maladroite. Pour gérer les réponses, vous devez inclure toute votre logique de réponse dans l'interception de la requête (au lieu de simplement intercepter une réponse, par exemple), puis vous devez interroger le corps à l'aide de l'ID d'interception. En effet, le corps peut ne pas être disponible au moment où votre gestionnaire est appelé et cela vous permet d'attendre explicitement ce que vous recherchez. Le corps peut également être codé en base64, vous devrez donc le vérifier et le décoder avant de le transmettre aveuglément.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

À ce stade, vous êtes libre de vous déchaîner sur JavaScript. Votre code vous place au milieu d'une réponse vous permettant à la fois d'accéder au JavaScript complet qui a été demandé et de renvoyer votre réponse modifiée. Génial! Nous allons simplement modifier le JS en ajoutant un console.log à la fin de celui-ci afin que notre terminal reçoive un message lorsque notre code modifié est exécuté dans le navigateur.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Nous ne pouvons pas transmettre uniquement un corps modifié, car ce contenu pourrait entrer en conflit avec les en-têtes envoyés avec la ressource originale. Comme vous testez et ajustez activement, commencez probablement par les éléments essentiels avant de vous soucier des autres informations d’en-tête à transmettre. Vous pouvez accéder aux en-têtes de réponse via ​responseHeaders transmis au gestionnaire d’événements si nécessaire, mais pour l’instant, créons un ensemble minimal dans un tableau pour faciliter leur manipulation et modification ultérieure.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

L'envoi de la nouvelle réponse nécessite la création d'une réponse HTTP complète codée en base64 (y compris la ligne d'état HTTP) et son envoi via une propriété rawResponse dans l'objet passé à continueInterceptedRequest .

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Maintenant, lorsque vous exécutez votre script et naviguez sur Internet, vous verrez quelque chose comme ce qui suit dans votre terminal pendant que votre script intercepte JavaScript et également pendant que votre JavaScript modifié s'exécute dans le navigateur et que les ​console.log() remontent à travers le hook que nous avons créé au début du didacticiel.

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Le code de travail complet pour l'exemple de base est ici :

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Où aller à partir d'ici

Commencez par mettre en forme le code source, c’est toujours un excellent point de départ pour la rétro-ingénierie. Vous pouvez le faire dans la plupart des navigateurs modernes, mais contrôlez chaque modification vous-même pour garantir la cohérence entre les navigateurs et leurs versions, et pour pouvoir relier les informations lors de votre analyse. Quand j’explore un code obfusqué ou étranger, je préfère renommer les variables et fonctions au fur et à mesure que j’en saisis le rôle. Modifier du JavaScript en toute sécurité est un travail délicat, digne d’un article à part entière, mais pour commencer, utilisez un outil comme ​unminify pour défaire les techniques courantes de minification et d’obfuscation.

Vous pouvez installer unminify via npm et envelopper votre nouveau corps JavaScript avec un appel à ​unminify afin de le voir en action :

Intercepter et modifier les réponses avec Chrome via le protocole Devtools

Nous approfondirons davantage les transformations dans le prochain article. Si vous avez des questions, des commentaires ou d’autres astuces intéressantes, n’hésitez pas à me contacter via Twitter !