Eksternalizacja klucza kodowania tablicy bajtkodu
Podaj własny klucz szyfrowania bajtkodu VM za pomocą vmBytecodeArrayEncodingKey i przekaż go z powrotem w czasie działania poprzez getter klucza — trzymany poza bundlem, odczytywany z magazynu klienta lub pobierany z Twojego backendu.
Co robią te opcje
vmBytecodeArrayEncoding szyfruje tablicę bajtkodu VM, dzięki czemu nie znajduje się ona w kodzie wynikowym jako zwykły tekst. Domyślnie klucz szyfrowania jest wyprowadzany ze środowiska i rekonstruowany po stronie klienta, więc nigdy nie masz z nim do czynienia. Jest to wygodne, ale materiał klucza wciąż znajduje się w bundlu.
Dwie opcje pozwalają wyjąć klucz z bundla i kontrolować go samodzielnie:
vmBytecodeArrayEncodingKey— klucz, który podajesz w czasie kompilacji. Gdy jest ustawiony, jest używany zamiast domyślnego klucza wyprowadzanego ze środowiska i nie jest osadzany w zaciemnionym kodzie wynikowym.vmBytecodeArrayEncodingKeyGetter— wyrażenie JavaScript, które zwraca ten sam klucz w czasie działania. Jest osadzane dosłownie i wykonywane w przeglądarce, gdy ładowany jest zaciemniony kod.
Chodzi o rozdzielenie: ponieważ klucz nie znajduje się w kodzie, czysto statyczne skanowanie bundla nie jest w stanie go odzyskać. Klucz musi jednak być obecny w czasie działania, 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ć.
Jak łączą się oba klucze
Twój klucz nigdy nie jest używany samodzielnie — po obu stronach jest mieszany z wewnętrznym kluczem kontrolowanym przez obfuskator:
- Czas kompilacji.
vmBytecodeArrayEncodingKeyjest łączony z wewnętrznym kluczem wyprowadzanym przez obfuskator, a tablica bajtkodu jest kodowana wynikowym kluczem mieszanym. - Czas działania. Wartość, do której rozwiązuje się Twój
vmBytecodeArrayEncodingKeyGetter, jest łączona z tym samym wewnętrznym kluczem, rekonstruowanym po stronie klienta na podstawie różnych czynników czasu działania, aby zdekodować bajtkod.
Ponieważ obie strony mieszają Twój klucz z kluczem wewnętrznym, getter musi rozwiązywać się do dokładnie tego samego ciągu znaków, który przekazałeś jako vmBytecodeArrayEncodingKey. Żaden z tych elementów nie wystarcza sam z siebie: Twój klucz bez klucza wewnętrznego nie zdekoduje bajtkodu, 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 działania
Domyślnie getter jest synchroniczny: wyrażenie musi zwrócić klucz natychmiast, gdy ładowany jest zaciemniony kod. Odczytaj go z dowolnego źródła, które jest już obecne po stronie klienta — z ciasteczka, localStorage, zmiennej globalnej lub elementu DOM wstrzykniętego przez serwer.
JavaScriptObfuscator.obfuscate(sourceCode, {
vmObfuscation: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: process.env.VM_KEY, // e.g. 'mySecretKey123'
vmBytecodeArrayEncodingKeyGetter: "window.__VM_KEY__" // returns the key at runtime
});
Klucz musi istnieć zanim uruchomi się zaciemniony kod:
// Set by a different script, a server-injected inline script, etc.
window.__VM_KEY__ = 'mySecretKey123';
Inne synchroniczne źródła działają tak samo — wybierz to, które Twoja aplikacja już wypełnia:
// From a cookie
vmBytecodeArrayEncodingKeyGetter: "document.cookie.match(/vmKey=([^;]+)/)?.[1]"
// From localStorage
vmBytecodeArrayEncodingKeyGetter: "localStorage.getItem('vmKey')"
// From a server-injected meta tag
vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]').content"
// From a nested object
vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"
Pobieranie klucza z Twojego 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 — tak abyś mógł ograniczyć do niego dostęp za pomocą uwierzytelniania i go unieważnić — getter musi być asynchroniczny, a to wymaga vmAsyncExecutor. Po włączeniu asynchronicznego executora getter może zwrócić Promise, a VM czeka na niego przed uruchomieniem.
JavaScriptObfuscator.obfuscate(sourceCode, {
vmObfuscation: true,
vmAsyncExecutor: true,
vmBytecodeArrayEncoding: true,
vmBytecodeArrayEncodingKey: process.env.VM_KEY, // kept on your server, not in the bundle
vmBytecodeArrayEncodingKeyGetter:
'fetch("/api/vm-key", { credentials: "include" }).then((res) => res.text())'
});
Na serwerze zdecyduj, który klucz zwrócić, na podstawie tego, czemu ufa Twoja aplikacja — ważnej sesji, oczekiwanego nagłówka Origin lub Referer, weryfikacji licencji itd. Sztuczka: zamiast odrzucać niezaufanych wywołujących, zwróć błędny klucz. Bajtkod dekoduje się wówczas do bezsensownych danych, a chroniony kod zawodzi sam z siebie, co jest bardziej dyskretne niż oczywisty 401, który mówi atakującemu dokładnie, co ma obejść.
// Express example — the exact checks depend on your app
app.get('/api/vm-key', (req, res) => {
const origin = req.get('origin');
const trusted =
req.session?.user && // a valid session, and
origin === 'https://app.example.com'; // the expected production origin
res.type('text/plain').send(
// Real key for valid users; a decoy for everyone else
// (no session, or a localhost / unexpected origin).
trusted ? process.env.VM_KEY : process.env.VM_DECOY_KEY
);
});
Zwracaj z tego punktu końcowego dokładnie ten sam ciąg znaków, który przekazałeś jako vmBytecodeArrayEncodingKey w czasie budowania. Kopia bundla uruchomiona poza Twoim środowiskiem otrzymuje klucz-wabik, deszyfruje się do niczego i pozostaje bezużyteczna.
Gdy klucz nie pasuje
Zaciemniony kod działa tylko wtedy, gdy getter zwraca dokładnie ten sam klucz, który został użyty podczas zaciemniania. Jeśli klucze się różnią — lub getter zwraca undefined, null albo pusty ciąg znaków — deszyfrowanie wytwarza błędny strumień klucza, a kod zawodzi w czasie działania, dając bezsensowny wynik lub zwykły błąd czasu wykonania.
Celowo nie ma osobnego, specyficznego dla klucza komunikatu o błędzie: nieudany klucz jest nie do odróżnienia od jakiejkolwiek innej awarii w czasie działania. Dlatego gdy chroniony przez VM bundle zgłasza błąd dopiero wtedy, gdy ta opcja jest w użyciu, sprawdź najpierw ścieżkę klucza — czy getter rozwiązuje się na stronie, zwraca niepusty ciąg znaków i zwraca tę samą wartość, z którą zbudowałeś kod.
