décharger et avant les événements et comment facilement les déboguer les deux!

tl; dr - si vous êtes seulement ici pour savoir comment définir un point d'arrêt et réellement déboguer les écouteurs d'événement de déchargement et de préchargement, allez directement à la section «L'astuce pour déboguer avec succès les écouteurs d'événement de déchargement / avant téléchargement» ci-dessous.

Comme je le fais souvent, je cherchais des vulnérabilités potentielles dans des sites Web et des applications Web bien connus. Il y en a un qui, selon moi, a des raisons de croire qu'il pourrait être vulnérable à une exploitation sérieuse de XSS.

À un moment donné du processus, j'ai réalisé que la partie susceptible d'être vulnérable à XSS est exécutée par un écouteur d'événement de déchargement enregistré par du code javascript sur le site Web. C'est là que j'ai dû déboguer pour pouvoir dire si j'ai trouvé quelque chose d'intéressant ici ou pas.

J'ai donc défini un point d'arrêt où se trouve le code censé être déclenché par l'événement unload, puis rechargé la page pour permettre à mon navigateur Google Chrome de s'arrêter à ce point d'arrêt, me permettant de le déboguer.

Pour ceux d'entre vous qui ont suffisamment d'expérience en javascript, ce qui suit ne vous choque pas du tout: le navigateur a complètement ignoré mon point d'arrêt et a rechargé la page normalement.

C’est un peu agaçant. Je veux dire, tout code javascript destiné à être exécuté dans le cadre de cet événement s’exécute parfaitement, qu’il s’agisse de console.log (), de localStorage.setItem () ou de toute autre action standard. Alors, qu'est-ce qui ne va pas avec mon point d'arrêt? ou placer un débogueur; commander? Pourquoi ça ne marche pas?

Afin de comprendre ce comportement, il est logique que nous comprenions d'abord ces événements. Le but de ces événements est de permettre à un site Web d'exécuter des actions préconfigurées juste avant, de décharger et lorsqu'il décharge.
 Ou, pour être plus précis, comme mentionné dans MDN: «L'événement beforeunload est déclenché lorsque la fenêtre, le document et ses ressources sont sur le point d'être déchargés» et «L'événement unload est déclenché lorsque le document ou une ressource enfant est en cours de déchargement. ”. (ce qui signifie qu’il ne s’agit pas tant du site Web que de toute fenêtre de ce dernier, qu’il s’agisse du cadre supérieur ou d’un iframe)

Essayons donc d’énumérer les différentes propriétés de ces deux événements et de les comparer:

Ordre: beforeunload sera toujours déclenché avant l'événement de déchargement (est logique, non?)

Objectif: L'événement beforeunload se déclenche juste avant le début du déchargement de la fenêtre. le déchargement se déclencherait pendant le déchargement de la fenêtre.

Cancelable: L'événement beforeunload peut être annulé par interaction de l'utilisateur:

L'exemple ci-dessus déclenchera une fenêtre demandant à l'utilisateur s'il est certain de vouloir quitter le site ou non. Si ce dernier décide de ne pas le quitter, l'événement beforeunload sera annulé, l'événement unload ne sera même jamais lancé et la page ne rechargera pas. En revanche, cela ne se produirait pas pour un événement de déchargement: une fois déclenché, il n’ya aucun moyen d’empêcher le déchargement de la fenêtre.

Objets supportés: les événements beforeunload et déchargement peuvent être enregistrés pour être écoutés par window, HTMLElementBody et HTMLFrameSetElement.

Il est à noter que les propriétés de déchargement des trois propriétés (la fenêtre, le corps et le jeu de cadres) pointent au même endroit (la même chose s'applique à beforeunload):

Oh, et pour ceux d’entre vous qui se demandent «qu'est-ce que le monde est un jeu de cadres?» - ne vous inquiétez pas. C'est cet élément très ancien et obsolète qui n'est plus du tout utilisé. En fait, il est actuellement défini comme obsolète, ce qui signifie qu'il pourrait être utilisé dans n'importe quelle nouvelle version de n'importe quel navigateur principal à tout moment!

Prototype: alors que le déchargement est un événement normal héritant d'Événement, beforeunload a son propre prototype, BeforeUnloadEvent. Ceci afin de mettre en œuvre la propriété unique returnValue de l'événement beforeunload, qui permet à l'écouteur d'événements enregistré d'afficher la boîte de dialogue "Êtes-vous sûr de vouloir quitter ce site?".

Alors, comment peut-on déboguer un écouteur d'événements avant / après un déchargement?

Et maintenant pour la partie cool! Comme je le disais au début, le débogage de ces événements est assez délicat parce que, quand on y réfléchit, on n’est pas censé pouvoir le faire. Le but des événements de déchargement et de préchargement est très clair: Exécutez un ensemble d’actions préconfigurées avant et au moment du déchargement d’une page. Toutes les actions synchrones sont garanties pour une exécution complète, mais s’agissant d’actions asynchrones initiées par des actions synchrones dans l’écouteur d’événements, le navigateur ressemble davantage à «Je ferai de mon mieux pour exécuter toutes vos actions, mais lorsque l’événement de déchargement termine je ne peux rien te promettre ».

Mais le navigateur accepte-t-il d'exécuter n'importe quel type d'action? La réponse est non. Il existe certains types d'actions qui, par spécification, ne peuvent pas être exécutées dans les écouteurs d'événements avant-déchargement / déchargement. alert () est un exemple, mais le plus pertinent est le débogueur; (ni définir un point d'arrêt à l'aide de devtools).

Donc, définir un point d'arrêt dans l'écouteur, définir un débogueur; déclaration ou tout autre élément réellement - serait ignoré par le navigateur, ce qui rendrait le débogage de l’événement unload / beforeunload impossible.

Le truc pour déboguer avec succès les auditeurs d'événement décharger / beforeunload

Revenons donc à mon histoire… Après quelques heures d’essai de déboguer avec succès l’auditeur d’événement de déchargement suspect, j’ai abandonné pour la nuit car il était très tard. Lorsque j'ai cliqué sur le petit bouton x pour fermer l'onglet, espérant qu'il s'éteindrait, j'ai été étonné de voir enfin mon point d'arrêt fonctionner!

Apparemment, il existe un flux de déchargement dans lequel le navigateur respecte les points d'arrêt et le débogueur; déclarations - pas une recharge normale de la page, mais une tentative réelle de fermer complètement son onglet!

Après cela, je me suis encore endormi car il était vraiment tard - mais depuis je peux facilement déboguer les auditeurs d'événements Décharger / Avant-déchargement!

Conseils pour les chercheurs
  1. Laissez un onglet supplémentaire ouvert dans votre fenêtre Chrome. Une fois que vous aurez utilisé cette astuce pour déboguer des écouteurs, vous ne pourrez pas empêcher leur débogage, mais vous ne pourrez pas empêcher l’onglet de s’éteindre. Il est plus facile de le faire plusieurs fois tout en travaillant dans la même fenêtre, ce qui ne serait possible que si l'onglet de fermeture n'est pas le seul onglet de la fenêtre.
  2. Navigateur Google Chrome: je n'ai pas vérifié si cette astuce fonctionnait sur d'autres navigateurs que Chrome, principalement parce que je ne voyais aucune raison de déboguer du code javascript dans un autre navigateur (oui, je l'ai dit).
     Cependant, cela pourrait fonctionner sur d'autres navigateurs - encore une fois, jamais même vérifié.
  3. Comprenez d'abord votre cas - si le code que vous essayez de déboguer est, pour une raison quelconque, basé sur une session, la fermeture de la session (en utilisant l'astuce ci-dessus et en fermant l'onglet) peut créer un flux différent de celui que vous essayez réellement de rechercher. - en tenir compte lors du débogage!
Conseils pour les développeurs
  1. Pas de longues actions à l'intérieur de ces auditeurs - bien que vous puissiez pratiquement faire de manière synchrone tout ce que vous voulez à l'intérieur de l'auditeur, c'est une mauvaise pratique! Évitez les actions longues afin d’empêcher l’utilisateur de décharger votre site Web en douceur.
  2. Utilisez sendBeacon () si vous devez envoyer des données avant le déchargement - il est parfois logique qu'un site Web veuille envoyer des données POST lorsque la page est sur le point de se décharger. Pour réussir, sendBeacon () a été inventé. (comme, sérieusement, il a été littéralement inventé pour ce cas), assurez-vous de l'utiliser si vous voulez savoir avec certitude que vos données sont envoyées sans attendre de réponse.
Pour résumer

J'espère que cela a eu un sens et vous a aidé à comprendre ces deux événements uniques, leur but et leurs différences. De plus, j'espère que vous trouverez cette astuce de débogage utile - c'est ce que j'ai fait!

Si j’ai manqué quelque chose ou si j’ai tort avec quelque chose de ce que j’ai dit dans cet article, je serais ravi de l’entendre pour pouvoir me corriger et que ce post soit aussi précis et utile que possible!

Oh, et désolé pour aucune démonstration jsfiddle .. pour une raison quelconque, les événements avant et après le déchargement et le déchargement ne fonctionnaient pas très bien avec.
 Je vous recommande de simplement copier-coller les extraits ci-dessus dans la console devtools de Chrome sur n’importe quel site Web et de les consulter par vous-même.

Edit: La méthode intégrée pour déboguer tous les écouteurs d’événements enregistrés dans chrome devtools m’a été signalée comme une autre façon de déboguer tous les écouteurs d’événements beforeunload. C'est en effet une excellente façon de le faire, mais:

  1. Il ne peut être utilisé que pour déboguer un événement beforeunload, alors que le tour que je vous présente ici vous permet également de déboguer un événement déchargé.
  2. Cela vous permet seulement de déboguer tous les écouteurs enregistrés, ce qui est très ennuyeux et inutile dans les sites Web complexes où il y a beaucoup d'écouteurs d'événements enregistrés et pour lesquels vous souhaitez déboguer un seul.

A été écrit par Gal Weizman.