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'à une rupture franche sur un bundle dérobé.
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.
Depuis la v7.1.0, 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, empoisonner, ou ne rien faire localement.
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 :
<script>
// In your page, BEFORE the obfuscated script:
window.__vmDetection = function (signal) {
navigator.sendBeacon('/api/vm-defense', JSON.stringify(signal));
};
</script>
<script src="/app.obfuscated.js"></script>
É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) :
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
// the hook alone enables nothing - a defense must be on for detectors to run:
vmSelfDefending: true,
vmDebugProtection: true,
vmDefenseHook: { name: '__vmDetection' }
});
La forme « chaîne simple » (vmDefenseHook: '__vmDetection') reste acceptée comme raccourci de { name: '__vmDetection' }, mais elle est dépréciée - préférez la forme objet.
Dans le tableau de bord, le champ VM Defense Hook apparaît dans le panneau d'options VM 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,domain,debugger,sandbox,nativeHook,timingouintegrity. Depuis la v7.4.0, les anciens détecteursenvetinspectorremontent soussource: 'debugger'.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 :
app.post('/api/vm-defense', express.text({ type: '*/*' }), (req, res) => {
// a signal: { source: 'headless', category: 'automation', score: 7, threshold: 4 }
const signals = [].concat(JSON.parse(req.body));
for (const signal of signals) {
console.warn('vm-defense', { ...signal, ip: req.ip, ua: req.get('user-agent') });
}
res.sendStatus(204);
});
Renommer les champs du signal (aliases) v7.4.0+
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.
vmDefenseHook: {
name: '__vmDetection',
aliases: {
source: { key: 'a8Qm', values: { headless: 'xP4m9Q' } },
category: { key: 'p3Tx', values: { automation: 'bQ7s1M' } },
score: { key: 's1' },
threshold: { key: 't1' }
}
// the callback now receives e.g. { a8Qm: 'xP4m9Q', p3Tx: 'bQ7s1M', s1: <score>, t1: <threshold> }
}
Dans le tableau de bord, la section Alias des signaux se trouve sous le champ VM Defense Hook.
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ésnone- 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 :
// built-in defaults
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
}
default s'applique à toutes les catégories, y compris celles qui sont correctes par construction (integrity, tamper) : { default: 'none' } produit donc un build véritablement non intrusif, purement télémétrique :
vmDefenseReaction: { default: 'none' } // never break - pair with vmDefenseHook
vmDefenseReaction: { automation: 'none' } // tolerate automation FPs; the rest keep their defaults (a bad domain still breaks)
Dans le tableau de bord, les sélecteurs VM Defense Reactions apparaissent dans le panneau d'options VM 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.
De la télémétrie à l'application effective
Vous n'êtes pas obligé de choisir dès le premier jour entre visibilité et application effective. Déployez les défenses en deux builds : un premier 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 - simplement, plus rien ne casse :
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
vmSelfDefending: true,
vmDebugProtection: true,
vmDomainLock: ['example.com'],
vmDefenseHook: '__vmDetection',
vmDefenseReaction: { default: 'none' } // observe only
});
É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 dont l'attaquant n'a plus à se soucier.
É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 - toute la bascule tient en cette seule ligne. Si une catégorie continue de produire des faux positifs que vous ne parvenez pas à éliminer, ne laissez que celle-ci à none (par exemple vmDefenseReaction: { automation: 'none' }) et appliquez les réactions pour toutes les autres.
