Documentazione
/
Ricette
/

Chiave di Bytecode Array Encoding

Esternalizzare la chiave di Bytecode Array Encoding

Pro

Fornisci una tua chiave di cifratura del bytecode della VM con vmBytecodeArrayEncodingKey e restituiscila durante l'esecuzione tramite un key getter, tenendola fuori dal bundle, leggendola dallo storage del client o recuperandola dal tuo backend.

Guarda

Obfuscator.io Async Executor: Async Bytecode Key Getter and Async-Only VM Virtualization

Guarda su YouTube

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

Guarda su YouTube

Cosa fanno queste opzioni

vmBytecodeArrayEncoding cifra l'array di bytecode della VM, in modo che non resti nell'output in chiaro. Per impostazione predefinita la chiave di cifratura viene derivata dall'ambiente e ricostruita sul client, quindi non devi mai gestirla. È comodo, ma il materiale della chiave resta comunque nel bundle.

Due opzioni ti permettono di togliere la chiave dal bundle e di controllarla tu stesso:

  • vmBytecodeArrayEncodingKey - la chiave che fornisci in fase di compilazione. Se impostata, viene usata al posto della chiave predefinita derivata dall'ambiente e non viene incorporata nell'output offuscato.
  • vmBytecodeArrayEncodingKeyGetter - un'espressione JavaScript che restituisce quella stessa chiave durante l'esecuzione. Viene incorporata così com'è e valutata nel browser quando il codice offuscato viene caricato.

Lo scopo è la separazione: poiché la chiave non si trova nel codice, un'analisi puramente statica del bundle non può recuperarla. Deve comunque essere presente durante l'esecuzione perché il codice funzioni, quindi non è davvero segreta, ma sei tu a decidere da dove proviene e chi può vederla.

Queste due opzioni vanno usate in coppia, e l'offuscatore lo impone: impostarne una senza l'altra è un errore di convalida in fase di build (vmBytecodeArrayEncodingKey da sola non ha alcun modo di ottenere la chiave durante l'esecuzione; un getter da solo non ha una chiave di compilazione con cui concordare). Inoltre non hanno alcun effetto se l'array di bytecode non viene effettivamente cifrato, quindi deve essere attivo anche vmBytecodeArrayEncoding: true, oppure vmSelfDefending: true, che forza internamente l'attivazione di vmBytecodeArrayEncoding. Imposta entrambe le chiavi insieme a una di queste due opzioni.

Come si combinano le due chiavi

La tua chiave non viene mai usata da sola: da entrambe le parti viene combinata con una chiave interna controllata dall'offuscatore:

  • In fase di compilazione. vmBytecodeArrayEncodingKey viene combinata con una chiave interna derivata dall'offuscatore e l'array di bytecode viene codificato con la chiave mista risultante.
  • Durante l'esecuzione. Il valore restituito dal tuo vmBytecodeArrayEncodingKeyGetter viene combinato con la stessa chiave interna, ricostruita sul client a partire da vari fattori di runtime, per decodificare il bytecode.

Poiché entrambe le parti combinano la tua chiave con la chiave interna, il getter deve restituire esattamente la stessa stringa che hai passato come vmBytecodeArrayEncodingKey. Nessuna delle due parti è sufficiente da sola: la tua chiave senza la chiave interna non può decodificare il bytecode, e la chiave interna è inutile senza la tua. Per questo è il controllo su chi riceve la tua chiave a proteggere davvero il codice.

Fornire la chiave durante l'esecuzione

Per impostazione predefinita il getter è sincrono: l'espressione deve restituire la chiave immediatamente quando il codice offuscato viene caricato. Leggila da una qualsiasi fonte già presente sul client: un cookie, localStorage, una variabile globale o un elemento DOM inserito dal server.

JavaScript

La chiave deve esistere prima che venga eseguito il codice offuscato:

JavaScript

Le altre fonti sincrone funzionano allo stesso modo: scegli quella che la tua app già popola:

JavaScript

Non mettere la chiave nello stesso file o script del codice offuscato. Inserirla lì vanifica completamente lo scopo: un'analisi statica del bundle recupererebbe sia il codice sia la sua chiave. Conservala in una fonte separata e inserisci la vmBytecodeArrayEncodingKey di compilazione da una variabile d'ambiente o da un segreto, invece di includerla in un commit.

Recuperare la chiave dal tuo backend (asincrono)

Richiede vmAsyncExecutor · v7.3.0+

Un getter sincrono può leggere solo ciò che è già presente sul client. Per recuperare la chiave dal tuo server, così da poterla proteggere con l'autenticazione e revocarla, il getter deve essere asincrono, e questo richiede vmAsyncExecutor. Con Async Executor attivo, il getter può restituire una Promise e la VM la attende prima di essere eseguita.

Attivare vmAsyncExecutor restringe anche ciò che viene virtualizzato: in questa modalità solo le funzioni async più esterne vengono compilate nella VM, mentre il codice sincrono non viene virtualizzato (il resto dell'offuscamento si applica comunque). Se il tuo programma è per lo più sincrono, racchiudi il codice che vuoi proteggere in una funzione async in modo che venga comunque coperto; consulta vmAsyncExecutor per la regola completa.

JavaScript

Un getter che restituisce una Promise richiede vmAsyncExecutor. Questo non può essere verificato in fase di build, quindi un getter con Promise e vmAsyncExecutor disattivato fallisce durante l'esecuzione.

Sul server, decidi quale chiave restituire in base a ciò di cui la tua applicazione si fida: una sessione convalidata, un controllo di licenza e così via. Origin o Referer da soli non autenticano un chiamante, e le richieste GET dalla stessa origine possono omettere Origin. Il trucco: invece di rifiutare i chiamanti non attendibili, restituisci una chiave sbagliata (VM_DECOY_KEY qui sotto). Il codice protetto allora fallisce da solo (errori, risultati errati o una pagina che smette di rispondere), il che è più discreto di un evidente 401 che dice a un attaccante esattamente cosa aggirare.

Disattiva la cache delle risposte, così la chiave di un chiamante non viene mai servita a un altro. Associa le chiavi alla versione corretta della build e distribuisci chiavi e bundle insieme. Un client che riceve la chiave reale può comunque esaminarla durante l'esecuzione.

JavaScript

Servi da questo endpoint esattamente la stessa stringa che hai passato come vmBytecodeArrayEncodingKey in fase di build. Il getter qui sopra recupera un URL relativo (/api/vm-key), quindi una copia del bundle ospitata su un'altra origine richiede /api/vm-key a quell'origine e non riceve mai la tua chiave; la chiave esca è ciò che riceve un chiamante che raggiunge il tuo endpoint ma senza una sessione attendibile (una richiesta non autenticata, un bundle sottratto e servito tramite proxy attraverso la tua origine). In entrambi i casi la chiave reale non arriva mai e il codice protetto non viene eseguito.

Cosa sia considerato "valido" dipende interamente dall'applicazione: una sessione autenticata, una licenza firmata o una qualsiasi combinazione. Qualunque cosa produca la chiave, racchiudila in una Promise e l'executor asincrono la attenderà prima di eseguire la VM.

Quando la chiave non corrisponde

Il codice offuscato funziona solo quando il getter restituisce esattamente la stessa chiave usata durante l'offuscamento. Se le chiavi sono diverse, o se il getter restituisce undefined, null o una stringa vuota, il codice fallisce durante l'esecuzione: produce risultati errati, genera un normale errore di runtime o smette di rispondere.

Non esiste deliberatamente un messaggio di errore distinto e specifico per la chiave: una chiave errata è indistinguibile da qualsiasi altro errore di runtime. Quindi, quando un bundle protetto dalla VM genera errori, restituisce risultati errati o si blocca solo da quando usi questa opzione, controlla prima il percorso della chiave: che il getter venga risolto nella pagina, restituisca una stringa non vuota e restituisca lo stesso valore usato per la build.