Telemetria i reakcje zabezpieczeń VM
Raportuj wykrycia zabezpieczeń VM do swojego backendu za pomocą vmDefenseHook i dostosuj reakcję na każdą kategorię wykryć przy użyciu vmDefenseReaction - od w pełni nieprzerywającej działania, wyłącznie telemetrycznej wersji po taką, która natychmiast przerywa działanie na skradzionym bundle'u.
Obejrzyj
Obfuscator.io Defense Reactions: Break, Decoy, and the VM Defense Hook
Problem
Zabezpieczenia VM - vmSelfDefending, vmDebugProtection i vmDomainLock - działają lokalnie: gdy zostanie wykryty debugger, narzędzie automatyzujące, zmodyfikowane środowisko lub nieautoryzowana domena, chroniony kod przerywa działanie albo po cichu zatruwa własne wyniki. To zatrzymuje atakującego, ale domyślnie nigdy się o tym nie dowiesz. Nie sposób stwierdzić, jak często bundle jest sondowany, który detektor zadziałał ani czy zabezpieczenie nie przerywa działania legalnemu użytkownikowi.
Tę lukę wypełniają dwie opcje. Żadna z nich nie włącza zabezpieczeń - jedynie obserwują i sterują tymi, które zostały już włączone:
vmDefenseHook- globalny callback otrzymujący obiekt sygnału za każdym razem, gdy zabezpieczenie coś wykryje. Służy do wysyłania telemetrii do backendu.vmDefenseReaction- mapa per kategoria, która wybiera, jak reaguje włączone zabezpieczenie: przerwać działanie, decoy albo nie robić nic lokalnie.
Obie opcje wprowadzono w wersji v7.1.0, ale każdy przykład na tej stronie używa formy obiektowej vmDefenseHook: { name }, która wymaga
v7.4.0. Wcześniejsze wersje przyjmowały sam ciąg znaków (vmDefenseHook: '__vmDetection'); od wersji v8.0.0 ta forma jest odrzucana, więc
wszędzie używaj formy obiektowej.
Przepis 1 - raportowanie wykryć do backendu
Krok 1 - zarejestruj globalną funkcję hooka, zanim wczyta się zobfuskowany bundle
Środowisko uruchomieniowe VM i jego zabezpieczenia działają przed chronionym programem, więc wiele wykryć następuje już podczas startu. Hook należy zdefiniować jako zwykłą zmienną globalną na stronie hosta, przed tagiem zobfuskowanego skryptu:
Krok 2 - wskaż go w vmDefenseHook
Opcja jest obiektem, w którym name to nazwa globalnej funkcji do wywołania (aliases jest opcjonalne - patrz niżej):
W panelu pole VM Defense Hook pojawia się w sekcji Ochrona zaawansowana, gdy włączone jest co najmniej jedno zabezpieczenie (vmSelfDefending, vmDebugProtection lub vmDomainLock).
Krok 3 - odbierz sygnał na backendzie
Każde wykrycie wywołuje hook z jednym obiektem signal:
source- konkretny detektor:headless,node,agent,agentBrowser,domain,debugger,sandbox,nativeHook,timinglubintegrity. Od wersji v7.4.0 dawne detektoryenviinspectorraportują jakosource: 'debugger'.agentBrowserraportuje w kategoriicategory: 'automation'i działa na targetach przeglądarkowych z włączonymvmDebugProtection(v7.9.0+).category-automation,debugger,sandbox,domain,tamperlubintegrity. Źródłonoderaportuje jakocategory: 'debugger'(v7.4.0+).score,threshold- wynik wykrycia i przekroczony próg
Minimalny endpoint odbierający (pokazano Express; zadziała dowolny backend przyjmujący POST). Normalizuje ciało żądania do tablicy, dzięki czemu obsługuje też formę zbiorczą wysyłaną przez opisany niżej wzorzec z buforem:
Hook służy wyłącznie do raportowania - jego wartość zwracana jest ignorowana, a brakujący lub rzucający
wyjątek hook nic po cichu nie robi. Nigdy nie może wyłączyć zabezpieczenia, więc atakujący nic nie zyska,
usuwając go lub psując. Aby zmienić to, co zabezpieczenie robi, należy użyć vmDefenseReaction (Przepis 2).
Zdefiniuj hook na stronie hosta, a nie wewnątrz zobfuskowanego kodu źródłowego
W telemetrii zależy Ci na przechwyceniu każdego wykrycia, a wiele z nich następuje już podczas startu - hook zdefiniowany wewnątrz zobfuskowanego bundle'a jest rejestrowany zbyt późno, aby je przechwycić, a jeśli zostanie skompilowany do VM, będzie nieosiągalny, dopóki program się nie uruchomi. W obu przypadkach pozostaje bezpieczny (brakujący hook nic nie robi, a hook, który sam wywołuje wykrycie, nie jest ponownie wywoływany rekurencyjnie), ale dla pełnego pokrycia należy zarejestrować go z góry na stronie hosta.
Jedynym wyjątkiem jest hook reagujący wyłącznie na wykrycie w czasie wykonania - na przykład sprzątanie, gdy debugger zostanie otwarty podczas użytkowania. Taki hook może znajdować się wewnątrz zobfuskowanego bundle'a; zobacz Przepis 3.
Aby mimo to chronić logikę raportowania, zarejestrowany hook powinien pozostać jednolinijkowym buforem opróżnianym z poziomu zobfuskowanego kodu:
Zmiana nazw pól sygnału (aliasy)
Domyślne wartości source / category to nazwy opisowe, więc każdy, kto podepnie się pod callback (lub przeczyta kod wynikowy), rozpozna zabezpieczenie i to, który detektor zadziałał. aliases zmienia nazwy pól sygnału na wybrane nieprzejrzyste tokeny, stosowane wewnątrz VM przed wyemitowaniem sygnału, dzięki czemu te nazwy nigdy nie pojawiają się w kodzie wynikowym ani nie docierają do callbacku. Aplikacja zna własne mapowanie i przekazuje tokeny do backendu.
Aliasy działają per pole: każde przyjmuje key (nazwę właściwości, którą otrzyma callback); tekstowe pola nazw source i category przyjmują dodatkowo mapę values, natomiast score / threshold są liczbami i przyjmują wyłącznie key. Nieustawione wpisy zachowują nazwy domyślne.
W panelu sekcja Aliasy sygnałów znajduje się pod polem VM Defense Hook.
To unikanie odcisku palca, a nie tajemnica - mapowanie nadal można wywnioskować przez wielokrotne testy, więc jedyną korzyścią jest brak ujawniania stabilnych, samoopisujących się nazw.
Przepis 2 - dostosowanie domyślnych reakcji
vmDefenseReaction konfiguruje sposób reakcji każdej kategorii wykryć. Niczego nie włącza - same zabezpieczenia włączają opcje vmSelfDefending, vmDebugProtection i vmDomainLock; ta opcja wybiera jedynie, jak reaguje włączone zabezpieczenie. Jednostką kontroli jest kategoria: każdy detektor w danej kategorii wykonuje reakcję tej kategorii, a reakcja ustawiona dla kategorii, której opcja jest wyłączona, po prostu nie ma efektu.
| Kategoria | Włączana przez | Reaguje, gdy |
|---|---|---|
automation | vmSelfDefending lub vmDebugProtection | Kodem steruje oprogramowanie, a nie człowiek: przeglądarka headless lub automatyzowana, framework do scrapowania / testowania albo agent AI do kodowania przechodzący krok po kroku przez stronę. |
debugger | vmDebugProtection lub vmSelfDefending | Ktoś ma otwarty debugger lub inspektor narzędzi deweloperskich przeglądarki i krok po kroku analizuje działający kod, aby go zrozumieć. |
sandbox | vmDebugProtection | Kod w ogóle nie działa w prawdziwej przeglądarce - został przeniesiony do emulowanego lub skryptowego środowiska JavaScript, aby uruchomić go i zbadać offline. |
domain | vmDomainLock | Kod działa w witrynie, która nie została autoryzowana: na hoście spoza listy dozwolonych vmDomainLock (na przykład bundle skopiowany na cudzą domenę). |
tamper | vmSelfDefending | Środowisko JavaScript wokół VM zostało zmodyfikowane, aby ją obserwować lub przejąć - na przykład natywne funkcje wbudowane przeglądarki podmieniono na wersje z instrumentacją. |
integrity | vmSelfDefending | Własny kod chronionego bundle'a został zmieniony lub załatany od czasu jego wygenerowania. |
Klucze to sześć powyższych nazw kategorii lub default (wartość zapasowa dla kategorii nieokreślonych). Wartości to:
break- natychmiast przerwać działaniedecoy- kontynuować działanie na zatrutym stanie, po cichu produkując błędne wyniki.decoywymagavmDebugProtectionlubvmDomainLockna targecie przeglądarkowym; w przeciwnym razie działa jakbreak.none- nie robić nic lokalnie (wyłącznie telemetria)
Kategoria, która nie została ustawiona, wraca do wbudowanych wartości domyślnych:
default obejmuje każdą kategorię, łącznie z integrity i tamper, więc { default: 'none' } daje naprawdę nieprzerywającą działania wersję wyłącznie z telemetrią:
W panelu listy wyboru VM Defense Reactions pojawiają się w sekcji Ochrona zaawansowana po włączeniu zabezpieczenia; każda kategoria jest edytowalna tylko wtedy, gdy włączone jest zabezpieczenie emitujące jej detektory.
Przepis 3 - uruchom własną logikę, zanim zabezpieczenie przerwie działanie
Hook służy nie tylko do raportowania - to również jedyne pewne miejsce, w którym można uruchomić własną reakcję zanim zadziała zabezpieczenie. Gdy na działającej stronie zostanie otwarty debugger, możesz chcieć wyczyścić to, co jest na ekranie, albo zastąpić widok stroną 404 - zanim kod przerwie działanie.
Dlaczego hook zamiast kodu w innym miejscu aplikacji: break zatrzymuje cały kolejny kod bajtowy, więc sprzątanie uruchamiane po zadziałaniu zabezpieczenia - zwłaszcza gdy samo jest zobfuskowane przez VM - to dokładnie to, czego przerwanie nie pozwala wykonać. Hook uruchamia się w miejscu wykrycia przed wykonaniem reakcji, synchronicznie - więc wywołana przez niego funkcja synchroniczna kończy się jako pierwsza, a potem break zatrzymuje VM.
Zdefiniuj reakcję jako swój vmDefenseHook. Ponieważ wykrycie debugger uruchamia się w czasie wykonania - po tym, jak program się wczytał i zdefiniował hook - hook może być częścią zobfuskowanego kodu źródłowego i zostaje skompilowany do kodu bajtowego razem z resztą bundle'a. Rozgałęziaj na podstawie signal.category, aby każdy warunek otrzymał właściwą reakcję, utrzymuj pracę synchroniczną, a następnie pozwól reakcji zadziałać:
To dotyczy wykryć, które następują w trakcie działania aplikacji - zobacz Kiedy kompilacja hooka do kodu bajtowego działa poniżej.
Pamiętaj o tych kwestiach:
- Tylko praca synchroniczna ma gwarancję ukończenia jako pierwsza. Reakcja uruchamia się na instrukcji tuż po powrocie z hooka. Wywołania typu fire-and-forget, które natychmiast oddają sterowanie, są w porządku (
navigator.sendBeacon, synchroniczne modyfikacje DOM i canvas); praca zaplanowana na później -setTimeout, kontynuacja promise'a,await- już nie, a cokolwiek, co wymaga dalszego kodu bajtowego VM, nie zostanie wykonane, bo właśnie to zatrzymujebreak. - Hook uruchamia się przed reakcją; jej nie zastępuje. Jego wartość zwracana jest ignorowana i nie może anulować, opóźnić ani zmienić tego, co robi reakcja. Służy do działania przed przerwaniem, a nie do jego zawetowania - aby zmienić samą reakcję, użyj
vmDefenseReaction(Przepis 2).
Kiedy kompilacja hooka do kodu bajtowego działa
Umieszczenie hooka wewnątrz zobfuskowanego bundle'a w ten sposób działa tylko dlatego, że wykrycie debugger uruchamia się w czasie wykonania. VM uruchamia vmDefenseHook, gdy jest jeszcze aktywna, po tym jak program się wczytał i zdefiniował hook, więc skompilowany do kodu bajtowego hook zostaje zdekodowany i uruchomiony jako pierwszy, a potem następuje break. To właśnie chroni kod źródłowy samego hooka.
Nie działa dla wykryć następujących podczas startu - automation, sandbox, domain lub debugger już otwarty w chwili wczytywania strony - ponieważ w tym momencie skompilowany do kodu bajtowego hook nie jest jeszcze zdefiniowany, więc zabezpieczenie nie znajduje żadnej funkcji do wywołania. W takich przypadkach zarejestruj hook jako zwykłą zmienną globalną na stronie hosta, tak jak w Przepisie 1. W razie wątpliwości zwykła zmienna globalna na stronie hosta obejmuje każde wykrycie, które dociera do hooka; kompilacja do kodu bajtowego dodaje ochronę jedynie dla kodu źródłowego samego hooka i tylko dla wykryć w czasie wykonania.
Od telemetrii do egzekwowania
Widoczność i egzekwowanie nie muszą być wdrażane razem. Mechanizmy obronne wdrażaj w dwóch etapach: najpierw wersję, która tylko raportuje, a potem - gdy telemetria wygląda czysto - taką, która reaguje.
Krok 1 - wypuść wersję wyłącznie obserwującą
Włącz wszystkie zabezpieczenia, których planujesz używać, wskaż w vmDefenseHook swój endpoint i wyłącz wszystkie reakcje. Każdy detektor nadal działa i raportuje każde trafienie do backendu, ale nic nie przerywa działania:
Krok 2 - przejrzyj zebrane sygnały
Gdy wersja zobaczy już prawdziwy ruch, poszukaj wykryć wywołanych przez legalne użycie. Dwa najczęstsze:
- trafienia
automationz własnych testów end-to-end lub monitoringu dostępności - te artefakty należy budować bez zabezpieczeń, zamiast tolerować całą kategorię na produkcji. - trafienia
domainz hosta staging lub podglądowego, którego nie uwzględniono na liście dozwolonychvmDomainLock- należy dodać ten host.
Lepiej usunąć przyczynę, niż złagodzić reakcję: każda kategoria pozostawiona na none to detektor, który atakujący może bezpiecznie zignorować.
Krok 3 - włącz reakcje
Wystarczy usunąć nadpisanie default: 'none', aby zaczęły obowiązywać wbudowane reakcje per kategoria; ta jedna linia to cała zmiana. Jeśli któraś kategoria nadal generuje fałszywe alarmy, których nie da się wyeliminować, należy pozostawić na none tylko ją (np. vmDefenseReaction: { automation: 'none' }), a resztę egzekwować.
Po włączeniu egzekwowania warto zostawić ustawiony vmDefenseHook - hook uruchamia się niezależnie od reakcji,
więc zachowujesz wgląd w to, kto sonduje Twój bundle, podczas gdy zabezpieczenia działają.
