Télémétrie et réactions des défenses VM
Remontez les détections des défenses VM vers votre backend avec vmDefenseHook, et réglez la réaction de chaque catégorie de détection avec vmDefenseReaction - depuis un build purement télémétrique, qui ne casse rien, jusqu'à un build qui rompt immédiatement sur un bundle dérobé.
Regarder
Obfuscator.io Defense Reactions: Break, Decoy, and the VM Defense Hook
Le problème
Les défenses de la VM - vmSelfDefending, vmDebugProtection et vmDomainLock - agissent localement : lorsqu'un débogueur, un outil d'automatisation, un environnement altéré ou un domaine non autorisé est détecté, le code protégé cesse de fonctionner ou empoisonne silencieusement ses propres résultats. Cela arrête l'attaquant, mais par défaut vous n'en saurez jamais rien. Impossible de savoir à quelle fréquence votre bundle est sondé, quel détecteur s'est déclenché, ni si une défense pénalise un utilisateur légitime.
Deux options comblent cette lacune. Aucune des deux n'active de défense - elles se contentent d'observer et d'orienter les défenses que vous avez déjà activées :
vmDefenseHook- un callback global qui reçoit un objet signal chaque fois qu'une défense détecte quelque chose. Utilisez-le pour envoyer de la télémétrie vers votre backend.vmDefenseReaction- une table par catégorie qui définit comment réagit une défense activée : rompre, leurrer ou ne rien faire localement.
Les deux options ont été introduites dans la v7.1.0, mais chaque exemple de cette page utilise la forme objet vmDefenseHook: { name }, qui nécessite
la v7.4.0. Les versions antérieures prenaient une simple chaîne (vmDefenseHook: '__vmDetection') ; cette forme est rejetée à partir de la v8.0.0 :
utilisez donc la forme objet partout.
Recette 1 - remonter les détections vers votre backend
Étape 1 - déclarer une fonction de hook globale, avant le chargement du bundle obfusqué
Le runtime de la VM et ses défenses s'exécutent avant votre programme protégé : de nombreuses détections se produisent donc au démarrage. Définissez le hook comme une simple globale dans la page hôte, avant la balise script obfusquée :
Étape 2 - faire pointer vmDefenseHook dessus
L'option est un objet dont le champ name désigne la fonction globale à appeler (aliases est facultatif - voir plus bas) :
Dans le tableau de bord, le champ VM Defense Hook apparaît dans la section Protection avancée dès qu'au moins une défense (vmSelfDefending, vmDebugProtection ou vmDomainLock) est activée.
Étape 3 - recevoir le signal sur votre backend
Chaque détection appelle le hook avec un unique objet signal :
source- le détecteur précis :headless,node,agent,agentBrowser,domain,debugger,sandbox,nativeHook,timingouintegrity. Depuis la v7.4.0, les anciens détecteursenvetinspectorremontent soussource: 'debugger'.agentBrowserremonte souscategory: 'automation'et s'exécute sur les cibles navigateur avecvmDebugProtection(v7.9.0+).category-automation,debugger,sandbox,domain,tamperouintegrity. La sourcenoderemonte souscategory: 'debugger'(v7.4.0+).score,threshold- le score de détection et le seuil qu'il a franchi
Un point de terminaison de réception minimal (exemple en Express ; n'importe quel backend acceptant un POST convient). Il normalise le corps en tableau afin de gérer également la forme groupée envoyée par le motif de tampon décrit ci-dessous :
Le hook sert uniquement à la remontée - sa valeur de retour est ignorée, et un hook absent ou qui lève une exception est
sans effet, en silence. Il ne peut jamais désactiver une défense : un attaquant qui supprimerait ou casserait votre hook n'y gagnerait rien. Pour changer ce que
fait une défense, utilisez vmDefenseReaction (recette 2).
Définissez le hook dans la page hôte, pas dans le source obfusqué
Pour la télémétrie, vous voulez capturer chaque détection, et beaucoup se produisent au démarrage - un hook défini à l'intérieur du bundle obfusqué est enregistré trop tard pour les intercepter, et s'il est compilé par la VM, il reste inaccessible tant que votre programme n'a pas démarré. Cela reste sans danger dans tous les cas (un hook absent est sans effet, et un hook qui déclenche lui-même une détection n'est pas rappelé de manière récursive), mais pour une couverture complète, enregistrez-le en amont dans la page hôte.
La seule exception est un hook qui réagit uniquement à une détection survenant à l'exécution - par exemple un nettoyage lorsqu'un débogueur s'ouvre pendant l'utilisation. Ce hook peut résider dans le bundle obfusqué ; voir la recette 3.
Pour protéger malgré tout votre logique de remontée, gardez comme hook enregistré un tampon d'une ligne et videz-le depuis votre code obfusqué :
Renommer les champs du signal (aliases)
Les valeurs source / category par défaut sont des noms descriptifs : quiconque instrumente le callback (ou lit la sortie) peut donc identifier la protection et le détecteur déclenché. aliases remplace les noms des champs du signal par des jetons opaques de votre choix, appliqués à l'intérieur de la VM avant l'émission du signal, de sorte que ces noms n'apparaissent jamais dans la sortie ni ne parviennent au callback. Votre application connaît sa propre correspondance et transmet les jetons à votre backend.
Les alias se définissent champ par champ : chacun accepte une key (le nom de propriété que reçoit le callback) ; les champs de type chaîne source et category acceptent en plus une table values, tandis que score / threshold sont des nombres et n'acceptent qu'une key. Les entrées non renseignées conservent leur nom par défaut.
Dans le tableau de bord, la section Alias des signaux se trouve sous le champ VM Defense Hook.
Il s'agit d'une gêne à l'identification, pas d'un secret - la correspondance peut toujours être déduite par tâtonnements répétés ; le seul bénéfice est de ne pas exposer des noms stables et explicites.
Recette 2 - ajuster les réactions par défaut
vmDefenseReaction détermine la réaction de chaque catégorie de détection. Cette option n'active rien - les défenses elles-mêmes s'activent via vmSelfDefending, vmDebugProtection et vmDomainLock ; elle ne fait que choisir la réaction d'une défense déjà activée. La catégorie est l'unité de contrôle : chaque détecteur d'une catégorie applique la réaction de cette catégorie, et une réaction définie pour une catégorie dont l'option est désactivée n'a tout simplement aucun effet.
| Catégorie | Activée par | Réagit lorsque |
|---|---|---|
automation | vmSelfDefending ou vmDebugProtection | Le code est piloté par un logiciel et non par une personne : navigateur headless ou automatisé, framework de scraping ou de test, ou agent de codage IA parcourant la page pas à pas. |
debugger | vmDebugProtection ou vmSelfDefending | Quelqu'un a ouvert un débogueur ou l'inspecteur des outils de développement du navigateur et parcourt le code en cours d'exécution pour le comprendre. |
sandbox | vmDebugProtection | Le code ne s'exécute pas du tout dans un vrai navigateur - il a été transposé dans un environnement JavaScript émulé ou scripté afin d'être exécuté et étudié hors ligne. |
domain | vmDomainLock | Le code s'exécute sur un site que vous n'avez pas autorisé : un hôte absent de votre liste d'autorisation vmDomainLock (votre bundle copié sur le domaine d'un tiers, par exemple). |
tamper | vmSelfDefending | L'environnement JavaScript autour de la VM a été modifié pour l'observer ou la détourner, par exemple en remplaçant des fonctions natives du navigateur par des versions instrumentées. |
integrity | vmSelfDefending | Le code du bundle protégé lui-même a été modifié ou patché depuis que vous l'avez généré. |
Les clés sont ces six noms de catégories, ou default (valeur de repli pour les catégories non spécifiées). Les valeurs possibles sont :
break- rompre immédiatementdecoy- continuer à s'exécuter sur un état empoisonné, en produisant silencieusement des résultats erronés.decoynécessitevmDebugProtectionouvmDomainLocksur une cible navigateur ; sinon, il se comporte commebreak.none- ne rien faire localement (télémétrie uniquement)
Une catégorie que vous ne renseignez pas retombe sur les valeurs par défaut intégrées :
default s'applique à toutes les catégories, integrity et tamper compris : { default: 'none' } produit donc un build véritablement non intrusif, purement télémétrique :
Dans le tableau de bord, les sélecteurs VM Defense Reactions apparaissent dans la section Protection avancée dès qu'une défense est activée ; chaque catégorie n'est modifiable que tant qu'une défense émettant ses détecteurs est active.
Recette 3 - exécuter votre propre logique avant qu'une défense ne rompe
Le hook ne sert pas qu'à la remontée - c'est aussi le seul endroit fiable pour exécuter votre propre réponse avant qu'une défense ne réagisse. Lorsqu'un débogueur s'ouvre sur une page en cours d'exécution, vous pourriez vouloir effacer ce qui est affiché à l'écran, ou remplacer la vue par une page 404, avant que le code ne rompe.
Pourquoi le hook plutôt que du code ailleurs dans votre application : break arrête tout le bytecode suivant, si bien qu'un nettoyage exécuté après le déclenchement d'une défense - surtout lorsqu'il est lui-même obfusqué par la VM - est précisément ce que la rupture empêche de s'exécuter. Le hook se déclenche sur le site de détection avant que la réaction ne soit appliquée, de façon synchrone - une fonction synchrone qu'il appelle se termine donc en premier, puis break arrête la VM.
Définissez la réponse comme votre vmDefenseHook. Comme la détection debugger se déclenche à l'exécution - une fois votre programme chargé et le hook défini - le hook peut faire partie de votre source obfusqué et est compilé en bytecode avec le reste du bundle. Aiguillez selon signal.category afin que chaque condition obtienne la bonne réponse, gardez le travail synchrone, puis laissez la réaction s'exécuter :
Cela s'applique aux détections qui se produisent pendant que votre application s'exécute - voir Quand la compilation du hook en bytecode fonctionne ci-dessous.
Gardez ces points à l'esprit :
- Seul le travail synchrone est garanti de se terminer en premier. La réaction s'exécute sur l'instruction qui suit immédiatement le retour du hook. Les appels « tire et oublie » qui délèguent immédiatement conviennent (
navigator.sendBeacon, modifications synchrones du DOM et du canvas) ; le travail que vous planifiez pour plus tard - unsetTimeout, une continuation de promesse, unawait- ne l'est pas, et tout ce qui nécessite davantage de bytecode VM ne s'exécutera pas, car c'est précisément ce quebreakarrête. - Le hook s'exécute avant la réaction ; il ne la remplace pas. Sa valeur de retour est ignorée, et il ne peut ni annuler, ni retarder, ni modifier ce que fait la réaction. Utilisez-le pour agir avant la rupture, pas pour y opposer un veto - pour changer la réaction elle-même, utilisez
vmDefenseReaction(recette 2).
Quand la compilation du hook en bytecode fonctionne
Placer ainsi le hook à l'intérieur du bundle obfusqué ne fonctionne que parce que la détection debugger se déclenche à l'exécution. La VM déclenche vmDefenseHook tant qu'elle est encore active, une fois votre programme chargé et le hook défini, de sorte que le hook compilé en bytecode est décodé et exécuté en premier, puis break. C'est ce qui protège le source du hook lui-même.
Cela ne fonctionne pas pour les détections qui se produisent au démarrage - automation, sandbox, domain, ou un débogueur déjà ouvert au chargement de la page - car, à ce moment-là, le hook compilé en bytecode n'est pas encore défini : la défense ne trouve donc aucune fonction à appeler. Pour celles-ci, enregistrez plutôt le hook comme une simple globale dans la page hôte, comme dans la recette 1. Dans le doute, une simple globale de la page hôte couvre chaque détection qui atteint le hook ; la compilation en bytecode n'ajoute de protection que pour le source du hook lui-même, et uniquement pour les détections à l'exécution.
De la télémétrie à l'application effective
La visibilité et l'application effective n'ont pas à être livrées ensemble. Déployez les défenses en deux étapes : d'abord un build qui se contente de remonter les détections, puis - une fois la télémétrie jugée saine - un second qui réagit.
Étape 1 - livrer un build en observation seule
Activez toutes les défenses que vous comptez utiliser, faites pointer vmDefenseHook vers votre point de terminaison et désactivez toutes les réactions. Chaque détecteur continue de fonctionner et de remonter chaque déclenchement vers votre backend, mais rien ne casse :
Étape 2 - examiner les signaux collectés
Une fois que le build a vu du trafic réel, cherchez les détections déclenchées par un usage légitime. Les deux plus fréquentes :
- des déclenchements
automationdus à vos propres tests de bout en bout ou à votre supervision de disponibilité - générez plutôt ces artefacts sans les défenses, au lieu de tolérer la catégorie en production ; - des déclenchements
domaindus à un hôte de préproduction ou de prévisualisation que vous avez oublié d'ajouter à la liste d'autorisationvmDomainLock- ajoutez cet hôte.
Privilégiez la correction de la cause plutôt que l'assouplissement d'une réaction : chaque catégorie laissée à none est un détecteur que l'attaquant peut ignorer sans risque.
Étape 3 - activer les réactions
Retirez la surcharge default: 'none' afin que les réactions intégrées propres à chaque catégorie s'appliquent ; cette seule ligne constitue tout le changement. Si une catégorie continue de produire des faux positifs que vous ne parvenez pas à éliminer, laissez cette seule catégorie à none (par exemple vmDefenseReaction: { automation: 'none' }) et appliquez les réactions pour toutes les autres.
Conservez vmDefenseHook une fois l'application effective activée - le hook se déclenche quelle que soit la réaction, ce qui vous permet de garder
de la visibilité sur qui sonde votre bundle pendant que les défenses agissent.
