Dokumentacja
/
Przepisy
/

Klucz Bytecode Array Encoding

Wyniesienie klucza Bytecode Array Encoding poza bundle

Pro

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

Obejrzyj na YouTube

Obfuscator.io Custom Bytecode Key: Compile-Time Key and Runtime Key Getter (Cookie, Fetch, Decoy Key)

Obejrzyj na YouTube

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ć:

  • vmBytecodeArrayEncodingKey to 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.
  • vmBytecodeArrayEncodingKeyGetter to 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. vmBytecodeArrayEncodingKey jest łą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.

JavaScript

Klucz musi istnieć, zanim zostanie uruchomiony zobfuskowany kod:

JavaScript

Inne źródła synchroniczne działają tak samo; wybierz to, które Twoja aplikacja już wypełnia:

JavaScript

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.

JavaScript

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.

JavaScript

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.