Wyniesienie klucza Bytecode Array Encoding poza bundle
Podaj własny klucz szyfrowania kodu bajtowego VM za pomocą vmBytecodeArrayEncodingKey i przekaż go z powrotem w czasie wykonania przez getter klucza. Klucz może pozostać poza bundle'em, być odczytywany z pamięci klienta lub pobierany z Twojego backendu.
Obejrzyj
Obfuscator.io Async Executor: Async Bytecode Key Getter and Async-Only VM Virtualization
Obfuscator.io Custom Bytecode Key: Compile-Time Key and Runtime Key Getter (Cookie, Fetch, Decoy Key)
Co robią te opcje
vmBytecodeArrayEncoding szyfruje tablicę kodu bajtowego VM, aby nie znajdowała się
w wyniku w postaci jawnej. Domyślnie klucz szyfrowania jest wyprowadzany ze środowiska i odtwarzany po stronie klienta, więc
nigdy nie musisz się nim zajmować. To wygodne, ale materiał klucza nadal znajduje się w bundle'u.
Dwie opcje pozwalają wyjąć klucz z bundle'a i samodzielnie nim zarządzać:
vmBytecodeArrayEncodingKeyto klucz, który podajesz w czasie kompilacji. Gdy jest ustawiony, zastępuje domyślny klucz wyprowadzany ze środowiska i nie jest osadzany w zobfuskowanym wyniku.vmBytecodeArrayEncodingKeyGetterto wyrażenie JavaScript, które zwraca ten sam klucz w czasie wykonania. Jest osadzane dosłownie i obliczane w przeglądarce podczas wczytywania zobfuskowanego kodu.
Chodzi o rozdzielenie: ponieważ klucza nie ma w kodzie, czysto statyczne przeszukanie bundle'a nie pozwala go odzyskać. Klucz nadal musi być dostępny w czasie wykonania, aby kod mógł się wykonać, więc nie jest naprawdę tajny, ale to Ty decydujesz, skąd pochodzi i kto może go zobaczyć.
Te dwie opcje tworzą parę i obfuskator to wymusza: ustawienie jednej bez drugiej jest błędem walidacji w czasie budowania
(sam vmBytecodeArrayEncodingKey nie daje sposobu na uzyskanie klucza w czasie wykonania; sam getter nie ma klucza z czasu
kompilacji, z którym mógłby się zgadzać). Nie działają też wcale, jeśli tablica kodu bajtowego nie jest faktycznie
szyfrowana, więc musi być włączone również vmBytecodeArrayEncoding: true - albo vmSelfDefending: true, które
wewnętrznie wymusza włączenie vmBytecodeArrayEncoding. Ustaw oba klucze razem z jedną z tych opcji.
Jak łączą się oba klucze
Twój klucz nigdy nie jest używany samodzielnie: po obu stronach jest mieszany z kluczem wewnętrznym kontrolowanym przez obfuskator:
- Czas kompilacji.
vmBytecodeArrayEncodingKeyjest łączony z kluczem wewnętrznym wyprowadzanym przez obfuskator, a tablica kodu bajtowego jest kodowana powstałym w ten sposób kluczem mieszanym. - Czas wykonania. Wartość, do której rozwiązuje się Twój
vmBytecodeArrayEncodingKeyGetter, jest łączona z tym samym kluczem wewnętrznym, odtwarzanym po stronie klienta z różnych czynników środowiska uruchomieniowego, aby odkodować kod bajtowy.
Ponieważ obie strony mieszają Twój klucz z kluczem wewnętrznym, getter musi zwracać dokładnie ten sam ciąg, który
przekazano jako vmBytecodeArrayEncodingKey. Żaden z elementów nie wystarcza samodzielnie: Twój klucz bez klucza
wewnętrznego nie odkoduje kodu bajtowego, a klucz wewnętrzny jest bezużyteczny bez Twojego. Dlatego to kontrola nad tym,
kto otrzymuje Twój klucz, faktycznie chroni kod.
Dostarczanie klucza w czasie wykonania
Domyślnie getter jest synchroniczny: wyrażenie musi zwrócić klucz natychmiast po wczytaniu zobfuskowanego kodu.
Odczytaj go z dowolnego źródła, które jest już dostępne po stronie klienta: pliku cookie, localStorage, zmiennej
globalnej lub elementu DOM wstrzykniętego przez serwer.
Klucz musi istnieć, zanim zostanie uruchomiony zobfuskowany kod:
Inne źródła synchroniczne działają tak samo; wybierz to, które Twoja aplikacja już wypełnia:
Nie umieszczaj klucza w tym samym pliku ani skrypcie co zobfuskowany kod. Osadzenie go tam niweczy cały sens: statyczne
przeszukanie bundle'a odzyskałoby zarówno kod, jak i jego klucz. Przechowuj go w osobnym źródle, a klucz
vmBytecodeArrayEncodingKey z czasu kompilacji wstrzykuj ze zmiennej środowiskowej lub sekretu, zamiast go commitować.
Pobieranie klucza z backendu (asynchronicznie)
Wymaga vmAsyncExecutor · v7.3.0+Synchroniczny getter może odczytać tylko to, co już znajduje się po stronie klienta. Aby pobrać klucz z Twojego serwera,
co pozwala uzależnić go od uwierzytelnienia i go unieważnić, getter musi być asynchroniczny, a to wymaga
vmAsyncExecutor. Przy włączonym asynchronicznym executorze getter może zwrócić
obiekt Promise, na który VM czeka przed uruchomieniem.
Włączenie vmAsyncExecutor zawęża też zakres wirtualizacji: w tym trybie do VM kompilowane są tylko najbardziej zewnętrzne
funkcje async, a kod synchroniczny nie jest wirtualizowany (pozostała obfuskacja nadal jest do niego stosowana). Jeśli Twój program jest w większości synchroniczny, opakuj kod,
który ma być chroniony, w funkcję async, aby nadal był objęty ochroną - pełną regułę opisuje
vmAsyncExecutor.
Getter zwracający Promise wymaga vmAsyncExecutor. Nie da się tego sprawdzić w czasie builda, więc getter zwracający
Promise przy wyłączonym vmAsyncExecutor zawodzi w czasie wykonania.
Na serwerze zdecyduj, który klucz zwrócić, na podstawie tego, czemu ufa Twoja aplikacja: zweryfikowanej sesji, sprawdzenia
licencji i tak dalej. Same nagłówki Origin lub Referer nie uwierzytelniają wywołującego, a żądania GET z tego samego
źródła mogą nie zawierać Origin. Haczyk polega na tym, że zamiast odrzucać niezaufanych wywołujących, zwracasz błędny
klucz (poniżej VM_DECOY_KEY). Chroniony kod sam wtedy przestaje działać (błędy, błędne wyniki lub strona, która przestaje
odpowiadać), co jest mniej widoczne niż oczywista odpowiedź 401, która dokładnie mówi atakującemu, co ma obejść.
Wyłącz buforowanie odpowiedzi, aby klucz jednego wywołującego nigdy nie trafił do innego. Powiąż klucze z właściwą wersją builda i wdrażaj klucze razem z bundle'ami. Klient, który otrzyma prawdziwy klucz, nadal może go podejrzeć w czasie wykonania.
Z tego endpointu serwuj dokładnie ten sam ciąg, który podczas builda przekazano jako vmBytecodeArrayEncodingKey. Powyższy
getter pobiera względny URL (/api/vm-key), więc kopia bundle'a hostowana w innym originie żąda /api/vm-key od tamtego
originu, więc nigdy nie otrzyma Twojego klucza; klucz-wabik otrzymuje wywołujący, który dociera do Twojego
endpointu, ale bez zaufanej sesji (nieuwierzytelnione żądanie, skradziony bundle przepuszczany przez Twój origin). Tak czy
inaczej prawdziwy klucz nigdy nie dociera, a chroniony kod się nie uruchamia.
To, co uznajesz za „prawidłowe”, zależy wyłącznie od aplikacji: uwierzytelniona sesja, podpisana licencja lub dowolne
ich połączenie. Niezależnie od tego, co wytwarza klucz, opakuj go w Promise, a asynchroniczny executor poczeka na niego
przed uruchomieniem VM.
Gdy klucz się nie zgadza
Zobfuskowany kod działa tylko wtedy, gdy getter zwraca dokładnie ten sam klucz, którego użyto podczas obfuskacji. Jeśli
klucze się różnią albo getter zwraca undefined, null lub pusty ciąg, kod zawodzi w czasie wykonania: daje błędne
wyniki, zgłasza zwykły błąd wykonania lub przestaje odpowiadać.
Celowo nie ma osobnego komunikatu błędu dotyczącego klucza: nieudany klucz jest nie do odróżnienia od dowolnego innego błędu wykonania. Dlatego jeśli bundle chroniony przez VM zgłasza błędy, zwraca błędne wyniki lub się zawiesza dopiero po włączeniu tej opcji, najpierw sprawdź ścieżkę klucza: czy getter rozwiązuje się na stronie, czy zwraca niepusty ciąg i czy zwraca tę samą wartość, z którą zbudowano kod.
