Quanto è facile hackerare JavaScript (in un browser)?


37

La mia domanda ha a che fare con la sicurezza JavaScript.

Immagina un sistema di autenticazione in cui stai utilizzando un framework JavaScript come Backbone o AngularJS e hai bisogno di endpoint sicuri. Questo non è un problema, poiché il server ha sempre l'ultima parola e controllerà se sei autorizzato a fare quello che vuoi.

E se fosse necessario un po 'di sicurezza senza coinvolgere il server? È possibile?

Ad esempio, supponiamo che tu abbia un sistema di routing sul lato client e desideri che un percorso concreto sia protetto per gli utenti che hanno effettuato l'accesso. Quindi esegui il ping del server chiedendoti se ti è permesso visitare percorsi protetti e vai avanti. Il problema è che quando esegui il ping del server, memorizzi la risposta in una variabile, quindi la prossima volta che vai su una route privata, controllerà che se hai già effettuato l'accesso (nessun ping al server) e a seconda sulla risposta andrà o no.

Quanto è facile per un utente modificare quella variabile e ottenere l'accesso?

La mia conoscenza della sicurezza (e JavaScript) non è eccezionale. Ma se una variabile non è nell'ambito globale ed è nella parte privata di un modello di modulo che ha solo getter ma non setter, anche in quel caso, puoi hackerare la cosa?


29
Tutte queste lunghe risposte. In breve, per rispondere alla tua intestazione, "molto hackable". Per rispondere alle tue prime 2 domande in linea tra loro, "senza server, hai perso la sicurezza" && "No". Per rispondere al terzo, "Molto facile", e infine "Sì, con facilità". Ecco qua Risposte a tutte le domande. : P
SpYk3HH,

Primo esempio, vedi il mio post sul blog sull'aggiunta di jQuery a qualsiasi pagina Web. spyk3lc.blogspot.com/2013/05/… Ora, giovane Padawan , vai avanti e provalo. Guarda quanto è facile aggiungere jQuery a qualsiasi sito che non lo abbia già, quindi usa jquery per manipolare molto facilmente qualsiasi parte della vista senza lunghe file di javascript. Boom!
SpYk3HH,

7
Una volta, durante la registrazione per un nome di dominio, il numero di telefono era un campo obbligatorio, ma questo era applicato solo nel javascript, non sul lato server. Quindi, l'ho disabilitato ridefinendo una funzione (usando Firebug) e voilà! Non hanno il mio numero di telefono.
Izkata,

8
manipulate any part of the sight without long lines site vs sight
mplungjan

8
I programmatori web professionisti devono fare la cosa giusta. Per favore. È più imbarazzante di una cosa grammaticale nazista :) plus.google.com/u/0/+MonaNomura/posts/h9ywDhfEYxT
mplungjan

Risposte:


26

Si prega di leggere la risposta di Joachim prima di leggere questo. Copre le ragioni generali alla base della vulnerabilità sul lato client. Ora, per un suggerimento su come aggirare questo problema ...

Uno schema sicuro per la comunicazione client-server senza dover eseguire l'autenticazione con il server manualmente su ogni richiesta:

Stai ancora lasciando che il server abbia l'ultima parola, e il server deve ancora convalidare tutto ciò che dice il client, ma succede in modo trasparente.

Supponiamo che il protocollo HTTPS prevenga gli attacchi man-in-the-middle (MITMA).

  • Il client stringe la mano al server per la prima volta e il server genera una chiave pubblica per il client e ne mantiene una privata utilizzando uno schema di crittografia asimmetrica. Il client memorizza la chiave "pubblica" del server nella memoria locale, crittografata con una password sicura che non si salva da nessuna parte.

  • Il client è ora offline. Il cliente desidera eseguire azioni attendibili. Il client immette la sua password e prende la chiave pubblica del server.

  • Il client ora esegue azioni in base alla sua conoscenza di tali dati e crittografa tutte le azioni che esegue con la chiave pubblica del server per quel client .

  • Quando il client è online, il client invia l'ID client e tutte le azioni eseguite dal client vengono inviate al server crittografate con la chiave pubblica del server.

  • Il server decodifica le azioni e, se sono nel formato corretto, si fida del fatto che hanno avuto origine nel client.

Nota:

  • Non è possibile memorizzare la password del client da nessuna parte, altrimenti un utente malintenzionato sarebbe in grado di recuperare la chiave e firmare le azioni come proprie. La sicurezza di questo schema si basa esclusivamente sull'integrità della chiave generata dal server per il client. Il client dovrebbe comunque essere autenticato con il server quando richiede quella chiave.

  • In realtà stai ancora facendo affidamento sul server per la sicurezza e non sul client. Ogni azione eseguita dal client deve essere convalidata sul server.

  • È possibile eseguire script esterni nei web worker . Tieni presente che ogni richiesta JSONP che hai ora è un problema di sicurezza molto più grande. È necessario proteggere la chiave a tutti i costi. Una volta perso, un utente malintenzionato può impersonare l'utente.

  • Ciò soddisfa la tua richiesta che non venga eseguito "nessun ping al server". Un utente malintenzionato non può semplicemente imitare una richiesta HTTP con dati falsi se non conosce la chiave.

  • La risposta di Joachim è ancora corretta . In effetti, stai ancora eseguendo tutta l'autenticazione sul server . L'unica cosa che hai salvato qui è la necessità di convalidare la password con il server ogni volta. Ora è necessario coinvolgere il server solo quando si desidera eseguire il commit o estrarre i dati aggiornati. Tutto ciò che abbiamo fatto qui è salvare una chiave attendibile sul lato client e far riconvalidare il client.

  • Questo è uno schema abbastanza comune per le applicazioni a pagina singola (ad esempio, con AngularJS).

  • Chiamo la chiave pubblica del server "pubblica" a causa di ciò che significa in schemi come RSA , ma in realtà sono informazioni sensibili nello schema e dovrebbero essere salvaguardate.

  • Non conserverei la password in nessun punto della memoria. Farei in modo che l'utente invii la sua password "offline" ogni volta che inizia a eseguire il codice offline.

  • Non eseguire il rollup della tua crittografia : utilizza una libreria nota come quella di Stanford per l'autenticazione.

  • Prendi questo consiglio così com'è . Prima di eseguire questo tipo di autenticazione in un'applicazione business-critical del mondo reale, consultare un esperto di sicurezza . Questo è un problema serio che è sia doloroso che facile da sbagliare.

È fondamentale che nessun altro script abbia accesso alla pagina. Ciò significa che permetti solo script esterni con i web worker. Non puoi fidarti di nessun altro script esterno che potrebbe intercettare la tua password quando l'utente la inserisce.

Utilizzare una prompte non un campo password in linea se non sei del tutto sicuro e non indugiare la sua esecuzione (che è, non dovrebbe vivere a un punto in cui gli eventi hanno accesso ad esso, ma solo nel codice di sincronizzazione). E in realtà non archiviare la password in una variabile - di nuovo, funziona solo se ritieni che il computer dell'utente non sia compromesso (anche se questo è vero anche per la convalida su un server).

Vorrei aggiungere ancora che non ci fidiamo ancora del cliente . Non puoi fidarti del cliente da solo, e penso che la risposta di Joachim lo inchioda. Abbiamo solo ottenuto la comodità di non dover eseguire il ping del server prima di iniziare a lavorare.

Materiale correlato:


3
"Don't roll your own crypto" si applica tanto ai protocolli quanto ai primitivi, se non di più, perché le persone di solito non sono abbastanza stupide da creare i propri primitivi, pensano di poter creare un protocollo che va bene . Inoltre non vedo perché RSA sia necessario qui. Dato che stai usando HTTPS, perché non generare semplicemente un segreto condiviso da 16-32 byte e richiedere che venga inviato con richieste future? Naturalmente, se lo fai, assicurati di usare l'uguaglianza invariante che controlla la stringa ...
Reid

2
+1 @Reid Sì, ho appena discusso di questo con un po 'di un esperto di questo recentemente. Ho basato approssimativamente lo schema primitivo qui su un protocollo che ho appreso (da Rabin IIRC) ma almeno fino a quando non riesco a trovare i dettagli (e giustificare ad esempio perché in questo caso è necessaria la crittografia asimmetrica). Non utilizzare lo schema suggerito in questa risposta senza prima consultare un esperto di sicurezza . Ho visto questo approccio usato in diversi luoghi, ma in pratica significa molto poco, se non altro. Ho anche modificato la risposta per migliorarla.
Benjamin Gruenbaum,

L'uso di un prompt aggiunge poca sicurezza. Un utente malintenzionato può ignorare il window.promptmetodo al fine di intercettare la passphrase o avviare un proprio prompt (possibilmente all'interno di un iframe!). Un campo password ha un ulteriore vantaggio: i caratteri non vengono visualizzati.
Rob W

@RobW Naturalmente, questo schema è inutile se la pagina è stata compromessa. Se l'attaccante ha accesso per eseguire JavaScript nella pagina siamo fregati. L'idea alla base di prompt era che si stava bloccando, ma hai ragione, ogni vantaggio di sicurezza che fornisce è dubbio. Vedi l'ultimo link nell'elenco.
Benjamin Gruenbaum,

1
Prima di tutto. Risposta fantastica, penso di non poter chiedere altro. D'altra parte, volevo fare progetti per animali domestici per me (e ovviamente per chi vuole usarlo) e forse questo è eccessivo (o forse no). Voglio dire, non voglio fare nulla di grave e temo di non poter ottenere quel tipo di autenticazione che mi hai spiegato, mancanza di conoscenza. Penso che eseguirò un semplice controllo 401 o se non ho alcuna richiesta, eseguo semplicemente il ping del server ogni volta che accedo a quel tipo di percorsi. Anche il token di autenticazione è un'opzione (e forse la cosa più vicina a ciò che hai spiegato qui AFAIK). Grazie :)
Jesus Rodriguez,

89

È semplice: qualsiasi meccanismo di sicurezza che si basa sul client per fare solo ciò che gli dici di fare può essere compromesso quando un utente malintenzionato ha il controllo sul client.

È possibile avere controlli di sicurezza sul client, ma solo per agire efficacemente come una "cache" (per evitare di fare un costoso di andata e ritorno al server se il client sa già che la risposta sarà "no").

Se si desidera mantenere le informazioni da un gruppo di utenti, assicurarsi che il client di tali utenti non ottenga mai tali informazioni. Se invii quei "dati segreti" insieme alle istruzioni "ma per favore non visualizzarli", diventerà banale disabilitare il codice che controlla quella richiesta.

Come vedi, questa risposta in realtà non menziona specifiche di JavaScript / Browser. Questo perché questo concetto è lo stesso, indipendentemente dal tuo cliente. Non importa che si tratti di un client fat (app client / server tradizionale), un'applicazione web di vecchia scuola o un'app a pagina singola con JavaScript esteso sul lato client.

Una volta che i tuoi dati lasciano il server, devi presumere che un utente malintenzionato abbia pieno accesso ad esso.


Grazie. Sarebbe bello se potessi spiegarlo un po 'di più, la mia conoscenza della sicurezza non è così buona e l'unica parte che capisco è che mi sbaglio: P. Quando si parla di memorizzazione nella cache, è necessario memorizzare nella cache solo quando il server dice di no? Voglio dire, se il server ha detto sì una volta, non puoi memorizzarlo nella cache, giusto?
Jesus Rodriguez,

3
Vorrei solo aggiungere che è possibile accedere alle variabili di chiusura nel browser (come nel modello del modulo menzionato), in particolare con un debugger :)
Benjamin Gruenbaum,

2
@BenjaminGruenbaum: sentiti libero di estenderlo in una risposta che si concentra sul lato JS delle cose. Ho dato solo una panoramica di alto livello e indipendente dalla tecnologia qui. Anche una risposta focalizzata su JS sarebbe piacevole!
Joachim Sauer,

1
Quindi, in breve, se alcune informazioni / route / qualunque cosa siano accessibili a seconda di una variabile javascript, ciò non sarà sicuro in ogni caso perché puoi facilmente cambiare quella variabile per dire ciò di cui hai bisogno per accedere a quella roba privata.
Jesus Rodriguez,

4
@JoachimSauer Penso che mancherebbe il punto di questa domanda che hai ben risolto. Indipendentemente dalle misure di sicurezza adottate dal client, è possibile compromettere la comunicazione . È possibile creare sistemi di autenticazione molto solidi in JavaScript, ma non vale nulla nel momento in cui si inserisce la comunicazione con un server che altri client considerano anche una fonte di verità. Il codice sorgente viene inviato al lato client, un utente malintenzionato intelligente può semplicemente leggerlo e imitare una richiesta HTTP al server. È necessario considerare qualsiasi cosa proveniente dal client come non attendibile se non convalidato sul server.
Benjamin Gruenbaum,

10

C'è un detto nella comunità di gioco: " Il cliente è nelle mani del nemico". Qualsiasi codice in esecuzione al di fuori di un'area protetta come il server è vulnerabile. Nello scenario più semplice, vulnerabile a non essere eseguito in primo luogo. È la decisione del cliente di eseguire effettivamente il" codice di sicurezza "e l'utente può semplicemente "opt out". Mentre con il codice nativo hai almeno un offuscamento automatico nell'assemblaggio e un ulteriore livello di protezione dal fatto che un utente malintenzionato deve essere un buon programmatore per manipolarlo, JS viene normalmente non confuso e come testo semplice. Tutto ciò di cui hai bisogno per mettere in scena un attacco sono strumenti primitivi come un server proxy e un editor di testo. L'attaccante avrà comunque bisogno di un certo livello di istruzione riguardo alla programmazione, ma è molto più semplice modificare uno script usando qualsiasi texteditor che iniettare codice in un eseguibile.


L'assemblea non è un offuscamento e chi crede altrimenti non ha mai giocato a un wargame
miniBill

@miniBill: Mentre un utente malintenzionato moderatamente esperto non avrà problemi con il codice assemblato, fornirebbe un filtro passa-alto di base. (Ahimè, questo è accademico, poiché estirpare i kiddies dello script fornisce un aumento minimo della sicurezza per lo scenario del PO)
Piskvor,

1

Questa non è una questione di hacking di JavaScript. Se volessi attaccare la tua app, utilizzerei un proxy privato che mi consentirebbe di acquisire, modificare e riprodurre il traffico. Il tuo schema di sicurezza proposto non sembra avere alcuna protezione in atto contro questo.


0

Parlando specificamente di Angular:

La protezione di una route client non esiste. Anche se 'nascondi' un pulsante per quel percorso in cui l'utente può sempre immetterlo, Angular si lamenterà, ma il client può codificarlo.

A un certo punto il controller dovrà richiedere al server i dati necessari per eseguire il rendering della vista, se l'utente non è autorizzato ad accedere a tali dati, non li riceveranno poiché sono stati protetti questi dati sul lato server e la tua app Angular dovrebbe gestire il 401 in modo appropriato.

Se stai cercando di proteggere i dati non elaborati, non puoi lato client, se desideri che un determinato utente sia in grado di visualizzare solo i dati comuni in un determinato modo, crea le "visualizzazioni" dei dati sul server anziché inviare dati grezzi per il client e averli riorganizzati (dovresti già farlo per motivi di prestazioni).

Nota a margine: non dovrebbero mai esserci elementi sensibili integrati nei modelli di vista richiesti da Angular, ma non farlo. Se per qualche motivo folle esiste, è necessario proteggere quei modelli di vista sul lato server, proprio come se si stesse eseguendo il rendering sul lato server.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.