Ispirato alla domanda di @😈╰•ALBERTO•╯😈 Come nascondere il codice JavaScript dal browser? e come promesso a @𝙏𝙝𝙚 𝙊𝙣𝙚 𝙁𝙧𝙤𝙢 𝙏𝙝𝙚 𝙑𝙖𝙪𝙡𝙩 ®, vediamo come oscurare il codice JavaScript, diverse soluzioni a confronto e soprattutto, analisi benchmark prestazioni!
Perché oscurare il codice JavaScript?
Per varie ragioni, si può scegliere di non voler rendere visibile il proprio codice (evitare che gli altri facciano copia-incolla con totale comprensione di ciò che noi abbiamo creato, ecc). Esistono diverse soluzioni per offuscare il codice JavaScript, che poi vediamo. Tutto ciò può avere anche degli effetti collaterali, sia in termini di performance (velocità di caricamento, a seconda di come andiamo ad offuscare il codice, le prestazioni - velocità e memoria - ne possono risentire quindi dipenda dal caso specifico), sia per un uso improprio che se ne potrebbe fare, facilità nel nascondere codice malevolo.
JSFuck: un progetto scherzoso ma... Interessante!
Si chiama davvero così, JSFuck è considerato più che un linguaggio un sottoinsieme "esoterico" di JavaScript, che concettualmente si avvicina allo stile di IOCCC, "programmazione creativa". Sito ufficiale JSFuck.com.
Da GitHub, il file README e LICENSE, ci dicono questo (rende l'idea 😁 ):
JSFuck is an esoteric and educational programming style based on the atomic parts of JavaScript. It uses only six different characters to write and execute code.
It does not depend on a browser, so you can even run it on Node.js.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Il principio di base di JSFuck è convertire ogni carattere in modo tale che in totale siano usati solamente sei caratteri ovvero: [,],(,),!,+. È ispirato a Brainfuck, tuttavia JSFuck è realmente un codice JavaScript valido e funzionante, questo lo rende molto interessante! Ad esempio, si ha questa equivalenza:
- 0 = false = ![]
- 1 = true = !![] oppure !+[]
- 2 = !![]+!![]
- 3 = !![]+!![]+!![]
- a = (![]+[])[+!+[]]
- e così via...
Vale a dire che, ad esempio i seguenti codici JavaScript vengono interpretati allo stesso identico modo! E ho inserito solo una minima sostituzione, altrimenti il codice diventa estremamente esteso e... illeggibile!
if(x%2==0){...}
if(x%(!![]+!![])==![]){...}
Come usare JSFuck: volendo ci si potrebbe creare un programmino per gestire le sostituzioni (sì, l'output è illeggibile ma trattandosi di equivalenza di linguaggio, tramite software si può risalire all'inverso, codice originale, volendo farlo). Comunque, avendolo testato, i consigli sono questi:
- andare sul sito ufficiale JSFuck.com
- sostituire una riga alla volta (o poco più), vista l'enormità di output che viene generato
- meglio avere un file testuale a parte, dove copiamo di volta in volta i pezzi generati. NON vanno inseriti spazi o andare a capo, altrimenti può generare errore, copiare l'output andando avanti così come viene generato
- salvare il file, che diventerà... dannatamente pesante!
Una soluzione può eventualmente essere quella di sostituire qualche pezzo e non tutto, come nell'esempio scritto prima if(x%(!![]+!![])==![]){...}
, direi già non proprio semplice da comprendere almeno per i "non addetti" ai lavori.
Per il benchmark di prestazioni, vediamo tutto alla fine, ora andiamo avanti con soluzioni alternative 😉
DaftLogic: Online JavaScript Obfuscator
Dal sito ufficiale daftlogic.com - Online Javascript Obfuscator, semplicemente incollare il proprio codice JavaScript all'interno dell'input box, premere "Obfuscate It" e il codice risultante sarà quello generato. Anche qui, a seconda dei casi, possiamo incollarne un pezzo alla volta.
Confronto, Benchmark prestazioni e conclusioni!
Come benchmark prestazioni, sono partito da un programma che risolve numericamente l'integrale di una semi-gaussiana con 107 iterazioni; lo stesso programma originale l'ho convertito con le varie soluzioni e abbiamo questi risultati:
- codice JavaScript originale: tempo di esecuzione circa 1,2 secondi, dimensione script 320 byte
- codice JavaScript JSFuck completo: tempo di esecuzione 1,2 secondi, dimensione script 19600 byte
- codice JavaScript JSFuck parziale (alcune sostituzioni a mano): tempo di esecuzione 1,2 secondi, dimensione script 531 byte
- codice JavaScript Online Obfuscator: tempo di esecuzione 1,6 secondi circa, dimensione 631 byte
Quindi facendo un confronto fra le soluzioni, abbiamo che JSFuck stravolge completamente la leggibilità del programma, lo script pesa molto in termini di dimensioni, tuttavia le operazioni richieste sono le stesse, le performance risultano invariate (se non forse piccoli rallentamenti a causa di dimensioni esagerate dello script, ma non in termini di elaborazione in sé). Una soluzione come JavaScript Online Obfuscator rende il codice comunque compatto, illeggibile, di fatto però le performance sono chiaramente sacrificate, come si vede da questo test. Avviene proprio una diversa valutazione del codice (e non mi sforzo per comprendere come 😅 ). Probabilmente si tratta anche di una soluzione difficile se non impossibile da decriptare, più sicura. Infatti viene fatto uso di anche soluzioni casuali e il software non fornisce la spiegazione dettagliata del proprio algoritmo (non ha mica interesse a farlo ahah), tuttavia occorre prestare attenzione alle performance.
In conclusione, potrei suggerire una soluzione come JavaScript Online Obfuscator per rendere indecifrabile il proprio codice, quando l'efficienza non è un fattore critico; JSFuck parziale (a mano oppure solo alcune parti del codice) per renderlo comunque illeggibile, senza che questo aumenti eccessivamente di dimensioni; oppure anche rendere manualmente "contorto" il proprio codice, pur mantenendo la stessa funzionalità e performance. Quando l'efficienza diventa un fattore critico (ciclo che calcola N iterazioni con N molto grande... Come nell'esempio fatto da me) valutare eventualmente anche la combinazione di diverse soluzioni di offuscamento, che non ci sia un danno in termindi di performance.
Cosa ne pensate? Idee e soluzioni alternative? 🙂
Per completezza riporto i vari codici dei programmi (eh sì, io mica ve li nascondo 😁 ):
- codice JavaScript originale:
var start=performance.now();
var ris=0;
var N=10**7;
var a=0;
var b=20;
var h=(b-a)/N;
function f(x){
return 1/(2*3.141592653589793)**0.5*2.718281828459045**(-0.5*x**2)
}
for(i=0;i<N;i++){
ris+=h*f(a+i*h)
}
var end=performance.now();
document.write("ris="+ris+"<br>"+(end-start)+"ms");
- codice JavaScript parzialmente offuscato (a mano) con JSFuck:
var start=performance.now();
var ris=+[];
var N=(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![])**(!![]+!![]+!![]+!![]+!![]+!![]+!![]);
var a=+[];
var b=!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![];
var h=(b-a)/N;
function f(x){
return +!![]/((!![]+!![])*3.141592653589793)**0.5*2.718281828459045**(-0.5*x**(!![]+!![]))
}
for(i=+[];i<N;i++){
ris+=h*f(a+i*h)
}
var end=performance.now();
document.write("ris="+ris+"<br>"+(end-start)+"ms");
- codice JavaScript totalmente offuscato con JSFuck (dato che pesa moltissimo, riporto solo un piccolo pezzo, visivamente è tutto così):
[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])+[])[+!+[]])+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])())
- codice JavaScript offuscato con JavaScript Online Obfuscator:
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('4 9=e.d();4 6=0;4 8=t**7;4 a=0;4 b=s;4 h=(b-a)/8;r f(g){q 1/(2*3.p)**0.5*2.o**(-0.5*g**2)}n(i=0;i<8;i++){6+=h*f(a+i*h)}4 c=e.d();m.l("6="+6+"<k>"+(c-9)+"j");',30,30,'||||var||ris||N|start|||end|now|performance||x|||ms|br|write|document|for|718281828459045|141592653589793|return|function|20|10'.split('|'),0,{}))