Come dimostrare che il lato client Javascript è sicuro?

8

Immagina di avere un'applicazione web che crittografa i dati dell'utente, come una nota o un foglio di calcolo, sia sul server che sul client.

Il normale processo per un utente che usa questa applicazione web è qualcosa del tipo:

  1. L'utente accede all'applicazione utilizzando un hash di login / password memorizzato sul server. (Come le normali applicazioni Web.)
  2. L'utente inserisce una chiave di sicurezza aggiuntiva che viene utilizzata per crittografare i dati del lato client. L'applicazione Web utilizza una libreria di crittografia lato client come SJCL

In questo esempio concentriamoci solo sul lato client.

La situazione è questa: il server è stato compromesso e un utente malintenzionato ha accesso alle chiavi lato server. L'utente malintenzionato non ha le chiavi laterali del client poiché non vengono mai memorizzate sul server.

Ora l'utente malintenzionato deve modificare JavaScript per leggere la chiave lato client quando l'utente lo inserisce nell'applicazione Web (lato client). Il Javascript sarebbe programmato per inviare la chiave al pirata informatico / server. Ora l'attaccante ha vinto.

Capisco che si presuma che una volta che si impadronisca del server, hai perso, ma mi piacerebbe sapere se i miei pensieri qui sotto consentono una soluzione sicura lato client.

La situazione

Si presume che l'HTML contenga qualche codice Javascript all'interno di alcuni tag di script e che ci sia anche un sacco di codice Javascript caricato tramite file Javascript esterni che risiedono sul server. È il Javascript che esegue l'applicazione web che è il problema. Dobbiamo supporre che l'autore dell'attacco abbia modificato qualsiasi Javascript, sia esso in linea o esterno.

Possibile soluzione?

Voglio essere in grado di generare un hash di tutti i Javascript caricati dal mio server. L'ha funzionerà come un'impronta digitale per il codice Javascript lato client e l'utente sarà diffidente nei confronti di un nuovo hash.

Questi sono i due modi in cui ho pensato finora:

  1. Prendere un hash di tutti i file caricati sul client. Ciò significa richiedere di nuovo tutti i file inclusi.

  2. Prendi un hash di tutto il codice Javascript in memoria. (Questo può anche essere fatto?)

Il problema comune con entrambe le opzioni è che qualsiasi funzione stia facendo questo hashing, deve essere abbastanza piccola da consentire all'utente interessato di verificare che sia sicuro da usare in pochi secondi.

Penso che questa funzione di hashing venga caricata nel browser come normale e che l'utente possa digitare il nome della funzione dalla console senza () in modo che possano vedere il codice, quindi digitare di nuovo con () per eseguire il codice.

Quindi l'hash dovrebbe essere sufficiente per dimostrare che l'applicazione web si trova in uno stato che l'utente sa di aver ispezionato in passato.

Questo potrebbe persino diventare un plugin a un certo punto, anche se sono determinato a vedere se è possibile una soluzione nativa.

In sostanza quello che sto chiedendo è, quali metodi esistono che ci permettono di dimostrare l'integrità dello stato del cliente?

    
posta Joseph 21.03.2016 - 12:59
fonte

5 risposte

8

Non puoi essere sicuro che non sia stato manomesso. Un utente malintenzionato sta eseguendo il codice sul tuo sistema - con sufficiente sforzo, è possibile manipolare qualsiasi cosa avvenga all'interno del contesto del browser in cui stai eseguendo (quindi, un plug-in non risente allo stesso modo - è in un contesto diverso). / p>

Non tutti i punti nel collegamento Matasano di @SmokeDispenser sono completamente corretti, sebbene il principio di base sia valido. Sforzi come l'API WebCrypto stanno cercando di risolvere alcuni dei problemi, ma non sono ancora maturi - anche se fossero , non sarebbe possibile determinare con certezza che il codice non stia facendo qualcosa di malevolo nello stesso momento in cui esegue il comportamento previsto.

    
risposta data 21.03.2016 - 13:11
fonte
4

Una pagina web con JavaScript al suo interno è essenzialmente una piccola applicazione che viene eseguita in una sandbox sul tuo computer. Ogni volta che visiti la pagina, scarichi la versione più recente dell'applicazione ed eseguila. ( fumetto XKCD obbligatorio )

Ciò significa che se un utente malintenzionato ha il controllo del proprio server e può fornire codice avvelenato, i problemi sono molto simili a quelli che l'utente ha scaricato da un sito di download malintenzionato. Eventuali protezioni che inserisci nella tua applicazione possono essere semplicemente rimosse o aggirate dall'autore dell'attacco.

L'unico modo per proteggere un'applicazione Web da un utente malintenzionato che controlla il server è se alcune parti della tua app Web sono archiviate sul computer dell'utente. Ad esempio, questo potrebbe essere un file scaricato o un data: URL bookmark. Questo pezzo di codice verrebbe caricato per primo e potrebbe quindi contenere una logica sufficiente a verificare l'integrità di tutte le risorse aggiuntive prima dell'esecuzione, ad es. tramite integrità subresource o nei vecchi browser che verificano l'hash prima di utilizzare exec() .

(Ho scritto una piccola implementazione sha256 per giocare con questa idea di bootstrap da un data: URL, e persino un modulo caricatore basato su di esso per divertimento, ma ovviamente non lo consiglieresti di usarlo effettivamente in produzione.)

In breve: se vuoi che i tuoi utenti inseriscano semplicemente un URL e caricino il tuo sito, questo dipende interamente dalla sicurezza del server. Anche il monitoraggio del tuo sito potrebbe non aiutarti se l'autore dell'attacco ha come target solo determinati utenti.

    
risposta data 21.03.2016 - 14:50
fonte
1

Se ti ho capito bene, vuoi assicurarti che il codice fornito dal server corrisponda a una nozione di riconosci-come-buono sul client. Ma per i browser, l'unico posto in grado di fornire contenuto al browser è il server, quindi i tuoi mezzi di convalida vengono forniti dalla stessa fonte e tramite lo stesso canale del contenuto che desideri convalidare (come ha detto Matthew).

C'è un certo margine per sfruttarlo a proprio vantaggio se è possibile separare il momento in cui le 2 parti vengono consegnate al client (cioè utilizzando tempi di cache differenti e ciascuna metà convalida l'altra). Ma sarà tutt'altro che infallibile.

Javascript fornisce una riflessione adeguata per rendere la convalida semplice (sì, puoi leggere cosa c'è nella memoria di Javacript). Il problema è la differenziazione tra il codice fornito come parte della pagina / caricato dalla pagina e ciò che è già incorporato nel browser. Quest'ultimo varierà per marca e versione. E finché il tuo codice richiama il codice fornito dal browser (ad esempio per scrivere cose sullo schermo) devi essere in grado di convalidare anche il codice del browser. Questo è un problema, dal momento che è semplice sostituire qualsiasi funzione javascript (compresi quelli incorporati) con qualcos'altro:

_orig_write = document.write;
document.write = function (str) {
    send_data_to_evil_site(str);
    _orig_write(str);
}

Non puoi fare affidamento sul rilevamento:

if ('function write() { [native code] }' != document.write.toString()) {
     alert("maybe the toString was changed too?");
}

Potresti dare un'occhiata al trasferimento del tuo javascript in file jar firmati . Sebbene originariamente previsto per fornire l'accesso Javascript al di fuori della sua sandbox, il meccanismo integrato nel browser per il vaildating del contenuto dovrebbe essere più robusto di una soluzione homegrown, ma ricorda ancora che questo codice può potenzialmente avere un impatto al di fuori della sandbox (che potrebbe essere un turn-off per tutti i clienti attenti alla sicurezza.

    
risposta data 21.03.2016 - 14:49
fonte
1

La convalida del codice lato client ha senso anche se il codice lato server non è stato compromesso. Se un utente malintenzionato è in grado di modificare il codice o iniettare un nuovo codice, può facilmente acquisire credenziali o modificare il markup della pagina e fare phishing, e questo è abbastanza grave da preoccupare per le persone.

Informazioni sulle soluzioni proposte finora:

  • Integrità delle risorse secondarie - convalida solo l'integrità del codice di terze parti e solo quando si carica. Un utente malintenzionato può iniettare codice in linea o avvelenare il codice esistente. Pertanto, l'SRI non è efficace contro questa particolare classe di attacchi. È pensato per rilevare quando la tua CDN è stata compromessa.
  • WebCrypto - è piacevole avere una crittografia standard nel browser, ma come qualsiasi altra funzione nativa disponibile, può essere avvelenata.
  • Altre soluzioni proposte si basano sul fatto che il loro codice viene eseguito prima di un possibile avversario. Il problema è che è molto difficile da assicurare. Ecco perché gli standard come CSP vengono trasportati nelle intestazioni HTTP e il browser ha per definizione l'obbligo di applicarli prima di caricare qualsiasi JS. (BTW, CSP non funziona anche contro l'avvelenamento da codice).

Non esiste una soluzione a prova di proiettile. Quello che puoi fare è alzare il tiro il più possibile, per mitigare la maggior parte degli attacchi e demotivare gli altri.

Sono sorpreso che nessuno abbia suggerito l'obsolescenza di JavaScript. Se l'offuscamento è sufficientemente elastico e persino polimorfico, può generare risultati che non è possibile comprendere e sufficientemente diversi. È possibile ruotare periodicamente le versioni protette per ottenere ciò. Con questo si eliminano gli obiettivi di avvelenamento automatico, poiché i nomi e le forme e persino il layout del codice continuano a cambiare costantemente. Suppongo che l'autore dell'attacco sia remoto rispetto al browser (da qui la necessità di automatizzare l'attacco). Inoltre, oggi esistono soluzioni che producono codice autodifesa che rende il codice resistente a manomissioni e avvelenamenti, il che rende sempre più complessa la sconfitta.

Per gestire in modo specifico le modifiche al DOM, è necessario qualcosa di leggermente diverso che sia in grado di rilevare queste modifiche e rimuoverle.

    
risposta data 08.02.2017 - 12:01
fonte
1

L'OP chiede se è possibile dimostrare che il lato client JavaScript è sicuro, nel caso in cui il server sia stato compromesso. Come notato da altri, finché il server fornisce al client il codice JavaScript, potrebbe essere manomesso, incluso il codice che serve a verificare che il codice sia sicuro.

Questo è già stato notato dall'OP, che suggerisce che l'ispezione del codice lato client potrebbe essere utilizzata per verificare:

I am thinking that this hashing function loads into the browser like normal, and the user can type the function name from the console without the () so they can see the code, and then type again with () to run the code.

Se la funzione di hashing è fornita dal server, questo è di nuovo facilmente aggirabile, prova a controllare la funzione harmful sotto nella console:

function harmful(){
  /*evil code*/
}

harmful.toString = function(){
  return 'function harmful(){/*I am harmless*/}'
}

Il punto principale è che non è possibile verificare la sicurezza del codice lato client in caso di compromissione del server, purché tutto codice lato client sia fornito dal server. E JavaScript è così flessibile che il codice dannoso può essere mascherato da innocuo durante l'ispezione del codice nella console.

    
risposta data 08.02.2017 - 12:36
fonte

Leggi altre domande sui tag