mercoledì 22 agosto 2012

Hashing delle password più forti in. NET con i fornitori universali di Microsoft


Il mese scorso ho scritto circa i nostri hashing delle password che senza vestiti che, per venire al sodo, hanno dimostrato come gli hash SHA salati (ad esempio creata dal provider di appartenenza ASP.NET), ha offerto quasi assenza di protezione da attacchi di forza bruta.Ho intenzione di assumere si ha familiarità con la storia di sfondo su questo (leggere l'articolo prima di questo in caso contrario), ma la linea di fondo era quella di hashing crittografico di password deve essere modo più lento. Non metà della velocità o anche un decimo della velocità, deve essere migliaia di volte più lento.
La conclusione del post era francamente, un po 'insoddisfacente. Perché? Perché in sostanza ha detto: "Se prendi il mio stack preferito utilizzare la tecnologia e l'implementazione di default per memorizzare le password, è insicuro". Sì, ho suggerito approcci alternativi ma questi non ha funzionato in modo nativo con il provider di appartenenze o richiesto l'accesso machine.config in modo che davvero non erano favorevoli al mondo di oggi di ottenere un app nella cloud in 5 minuti .
Ma si scopre che siamo sul punto di risolvere questo per ASP.NET e si può accedere a una soluzione migliore in questo momento. In realtà si può anche essere lo si utilizza già e solo che non lo so, perché fino ad ora, in realtà non è stata pubblicata.

I modelli di progetto sono morti - a lungo i modelli di progetto dal vivo!

Molto di questo ha a che fare con modelli di progetto e il modo migliore per illustrare ciò che sta cambiando è quello di dare un'occhiata a un progetto Web in Visual Studio 2010 e confrontarlo con uno nel 2012.
Cominciamo con un NET 4 Applicazione Web ASP.NET nella versione in uscita.:
Nuova Applicazione Web ASP.NET in Visual Studio 2010
Argh - i colori! Spostamento a destra lungo, ora faremo esattamente la stessa cosa in Visual Studio 2012. Si noti che questo è ancora di mira NET 4 così. in teoria , stiamo ottenendo la stessa cosa:
Nuova Applicazione Web ASP.NET in Visual Studio 2012
Solo che non sono la stessa cosa. Ecco quello che abbiamo ottenuto nel 2010, quando l'applicazione è stato eseguito:
Un Visual Studio 2010 modello predefinito
Ed ecco quello che si ottiene nel 2012:
Un Visual Studio 2012 modello predefinito
Questo non sarà una novità per molti di voi, molto è già stato discusso sui miglioramenti ai nuovi modelli. Sono più mobile friendly, si ottiene Modernizr e jQuery UI costruito in più tutti i componenti aggiuntivi sono ora NuGet pacchetti in modo che siano facili da aggiornare. Ci sono molte, tante buone ragioni per utilizzare i nuovi modelli, ma lasciate che vi mostri quello che non si è parlato molto.
Diamo uno sguardo alla configurazione del provider di appartenenze in ciascuno di questi progetti, in primo luogo nel 2010:
< add name = " AspNetSqlMembershipProvider " 
tipo = " System.Web.Security.SqlMembershipProvider "
Il provider di appartenenze SQL è il ragazzo che abbiamo usato per applicare sulla parte superiore di SQL Server (ovviamente) dopo l'esecuzione di aspnet_regsql per creare tutti gli oggetti DB appropriati. Ho fatto un screencast meraviglia 5 minuti su questo lo scorso anno e per la maggior parte, questo approccio ha funzionato bene. Significava il nostro livello di DB finito per assomigliare a questo:
Gli oggetti di database creati dal vecchio provider di appartenenze SQL
Per motivi di brevità vi risparmio da tutte le stored procedure (ce ne sono 55 di loro), gli schemi (13) e ruoli (altri 13). Come posso mettere questo bene ... ha fatto il database di un pasticcio sanguinoso. Non è solo il volume degli oggetti, sono i "ASPNET" prefissi e dipendenze un'epoca passata di stored procedure (sì, sì, lo so alcune persone discutere con me con veemenza su questo) che ha reso l'atmosfera DB come un po 'di scarico a terra.
Non lasciare che il soffermarsi su ciò, però, ecco cosa troverete nel 2012:
< add name = " DefaultMembershipProvider " tipo = " System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, 
Version = 1.0.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35 "
Scott Hanselman ha scritto System.Web.Providers lo scorso anno in relazione a un pacchetto NuGet che tornerò ad un po 'più tardi. Per ora, però, facciamo un po 'di uno sguardo la magia alla base di questo. In primo luogo, non c'è più aspnet_regsql, basta assicurarsi che la stringa di connessione è impostata e l'account dotato di diritti DBO (non ti preoccupare, non deve rimanere in questo modo), quindi eseguire l'applicazione e tenta di eseguire qualsiasi azione che dovrebbe causare il provider di appartenenze per colpire il DB (cioè il log - non importa che non ci sia un conto). Ora diamo uno sguardo verso l'oggetto che sono stati creati:
Le tabelle generate dal provider di appartenenze nuovo
Tutto qui. Nessun viste, stored procedure, non senza ruoli, senza schemi e nemmeno qualsiasi "ASPNET" prefissi. Questo è il modo in cui le persone normali costruire database! Ok, siamo in grado di discutere la pluralizzazione ma a parte questo, è una struttura DB ragionevole.
Prendiamo in considerazione le modifiche apportate dal vecchio provider di appartenenze SQL, vale a dire nella tabella che memorizza i dati di adesione (vecchio a sinistra, nuovo a destra):
SNAGHTML7dd625
Sono molto simili e francamente le omissioni non stanno andando a perdere da chiunque (una colonna dedicato per la posta elettronica in minuscolo -? Davvero). Ma è ciò che è memorizzato nel campo della password di questo tavolo che è il pezzo forte ...

Ciao a dire PBKDF2

Permettetemi di eseguire ogni applicazione e creare un'unica registrazione con la "password" password (non la gente, questo è solo un esempio!):
 SalePassword
2010SkFaUVXansqPe7lm9oHiQA ==UEWkR0OVsY6sNQuxFgyXCkpUvrY =
2012z1h310KsaE/FmxxXNHUiXg ==saRtN7Aju vqYfX25c1QpnxESIHsy1s6UiR5z7UwixM + =
Ok, quello che sta succedendo qui? Perché è l'hash nel modello 2012 saltato fuori? Algoritmi di hash creano sempre la stessa lunghezza cifra indipendentemente dalla stringa di input, si deve dire che qualcosa di diverso sta accadendo. E non vi è - ciò che vedete qui è il risultato del provider di appartenenze universale che ora è chiamata direttamente nel metodo Crypto.HashPassword che abbiamo avuto in System.Web.Helpers per un po 'adesso.
Una delle grandi cose su porzioni significative di ASP.NET attualmente in fase di open sourceè che è più facile che mai a dare un'occhiata sotto le coperte. Per coloro che vogliono vedere cosa sta succedendo, l'intera implementazione crittografia è finita su CodePlex . La cosa più importante per questo post, i commenti che lo rendono molto facile da capire cosa sta succedendo:
 / * =======================
 * FORMATI password hashing
 * =======================
 * 
 * Versione 0:
 * PBKDF2 con HMAC-SHA1, sale a 128-bit, 256-bit sottochiave, 1000 iterazioni.
 * (Vedi anche: SDL linee guida crittografia v5.1, Parte III)
 * Formato: {0x00, sale, sottochiave}
 * /
E non si tratta - il provider di appartenenze sta attuando 1000 iterazioni di SHA1 così dal punto di vista della forza bruta, questo impone un notevole aumento del carico di lavoro sulla implementazione di default nel vecchio provider di appartenenze SQL.
In questo post precedente debolezza hashing ho scritto, ci sono voluti 44 minuti e 56 secondi per risolvere il 63% delle password comuni in un campione di quasi 40.000. Se il campione ha utilizzato il nuovo provider partecipazione universale, che il tempo avrebbe fatto saltare fuori per più di un mese - 31,2 giorni, per l'esattezza. Dove ho già usato il paradigma della seduta attraverso un paio di episodi di The Family Guy, questo è come guardare La minaccia fantasma back to back 330 volte. Chiaramente questo rende l'esperienza dolorosa che molti scoraggiare un hacker determinato.
Ora, naturalmente, questo non è mai stato su come rendere le password indecifrabile e, come per la maggior parte gli aspetti di sicurezza del software si tratta di aumentare il grado di difficoltà ad un livello che lo rende più fattibile per un numero maggiore di persone. Sarà la barra sollevata, è improbabile che si crepa hacktivisti password da una corsa del mulino sito web? Sì, molto probabile. Ma si fermerà il governo degli Stati Uniti da craccare le password su un sito web facilitare l'attività terrorista? Non scommettere su questo.

L'impatto sulle prestazioni

Che cosa significa questo per le prestazioni? Quanto tempo ci vuole un accesso ora prendere? E sarà la profezia di utilizzo della CPU a livelli inaccettabili essere vero? Diamo uno sguardo.
Che cosa ho intenzione di fare è accedere al sistema 1.000 volte e vedere quanto tempo ci vuole con il vecchio provider e poi con il nuovo. Ecco cosa sta succedendo:
var = sw nuovo Cronometro ();
sw.Start ();
per ( var i = 0; i <1000; i + +)
{
  Appartenenza . ValidateUser ( "troyhunt" , "password" );
}
sw.Stop ();
passwordDuration.Text = sw.ElapsedMilliseconds.ToString ( "n0" );
Ora tenere a mente che non si può davvero fidarsi della classe Stopwatch , almeno non con un certo grado di accuratezza, ma questo è un bene per durate indicative. Ho eseguito lo script di cui sopra con entrambi i provider di appartenenze a volte manciata in IIS Express e una media di fuori (per inciso, i risultati sono stati molto simili quando si esegue direttamente sotto IIS). Ecco che cosa ho ottenuto:
 SqlMembershipProviderDefaultMembershipProvider
1000 iterazioni2941 ms102164 ms
Accessi al secondo34010
Ci sono un paio di cose interessanti da notare qui:
  1. Il nuovo provider è più lento, ma non 1000 volte più lento. Questo perché il processo di hash stessa è solo una piccola parte di accesso. Oltre a questo vi è anche la gestione delle connessioni ADO.NET, l'esecuzione di query (più su quello a breve) e le spese generali per altre lavorazioni oltre la semplice generazione di un hash.
  2. Dal punto di vista dell'utente, questo non è in alcun modo una durata troppo lungo per un accesso. Come per il punto precedente, c'è un mucchio di roba che succede quando si accede, ma la linea di fondo è che l'intero processo è in corso circa 100ms, che per una attività volta per sessione, è bene.
Ma per quanto riguarda l'impatto sulla CPU? Voglio dire in base alla progettazione di questi algoritmi di hashing hanno lo scopo di farlo funzionare piuttosto difficile, sta andando ad iniziare a causare picchi di brutto in termini di utilizzo efficace DoS'ing l'applicazione? Ecco quello che ho osservato in 1.000 corse sequenziali:
CPU a circa il 7,5%
Questo è usare circa il 7,5% della CPU per la costante, back to back hashing. Ora, naturalmente, questo è solo accadendo in un singolo thread e la storia inizia a cambiare abbastanza rapidamente con più esecuzioni simultanee. Sia come sia, abbiamo ancora registrato 1.000 persone in tutto circa un minuto e mezzo, quindi mi bastone mia affermazione dal post precedente che la popolarità di quel livello è un problema mi piacerebbe avere! Effettivamente essere coscienti del overhead aggiuntivo, ma è improbabile che averlo causare problemi a meno che non hai a che fare con scala serio.
Quindi, parlando di esecuzione delle query, ricorda come il vecchio fornitore ha creato tutte quelle procedure memorizzate (tra gli altri oggetti di DB)? Ecco cosa avveniva quando vi siete collegati:
Singola stored procedure per accedere al provider di appartenenze vecchio
Una stored procedure. Tutto qui. Andando avanti nel provider universale, ecco cosa sta succedendo alla fine SQL per un accesso singolo :
Otto istruzioni SQL utilizzate per accedere al proider nuove adesioni
DBA - mettere giù i forconi! Questo è dal RC di Visual Studio 2012 versione con 1.0 di fornitori universali e per fortuna le query eccessive sono qualcosa che Microsoft sa :
Erik Porter parlando, che fissa le richieste eccessive
Ok, è eccessivo, ma almeno i ragazzi sono sul caso e chiaramente le cose che stanno rettifica. Speriamo che sarete in cima a questa prima VS2012 entra nel vivo e la gente inizia a creare dipendenze più su di esso. Per fortuna, perché i nuovi modelli associare la libreria per NuGet, quando un aggiornamento è disponibile sta andando essere un processo di aggiornamento molto semplice davvero.
Per l'amor di interesse, l'implementazione corrente sta eseguendo due serie identiche di quattro domande che si presentano come segue:
  1. Selezionare il record utente e praticamente ogni colonna su di esso con il nome utente fornito al momento del login
  2. Selezionare un record utente tagliata verso il basso da parte dell'utente ID della query precedente (non so perché una seconda query è necessario ...)
  3. Aggiornare il LastLoginDate sul tavolo dei membri
  4. Aggiornare il LastActivityDate sul tavolo Utenti
La seconda query sembra un po 'ridondante e quindi ripetendo tutti e quattro sembra moltoridondante quindi speriamo che i miglioramenti che Erik si allude nella sua Tweet portare questo fino ad un molto più DB amichevoli tre query.
BTW - anche questo è un buon esempio di dove profiling query DB è molto importante, soprattutto quando ci sono astrazioni tra il codice e la query, quali i fornitori o un ORM.Lancia il SQL Profiler e assicurarsi di sapere cosa sta succedendo.

Meglio hashing per grandi e piccoli

Una delle grandi cose su i modelli che vengono confezionati in Visual Studio 2012 è una dipendenza crescente NuGet. Questa è una mossa molto cosciente da parte di Microsoft - meno dipendenza da componenti del framework di base che solo vengono aggiornate ogni pochi anni e più dipendenza da pacchetti che possono essere aggiornati e spinto in modo indipendente.
Quando diamo uno sguardo all'interno dei pacchetti già installati nel modello VS2012, ecco quello che c'è dentro:
NuGet pacchetti di Visual Studio 2012 modello
La buona notizia è che ora possiamo solo tornare subito su VS2010 e tirare i pacchetti stessi in:
Prendendo Microsoft.AspNet.Providers.Core da NuGet in Visual Studio 2010
O per i ninja della console:
PM> Install-Package Microsoft.AspNet.Providers.Core
Ora tutto ciò che facciamo è salto nel web.config e modificare il tipo di provider di appartenenze dal vecchio SqlMembershipProvider al DefaultMembershipProvider nuovo (solo i valori indicati in precedenza, più una trasformazione simile per il provider di ruoli, se si sta utilizzando), ha colpito il fidato F5 pulsante quindi tenta di accedere. Ora diamo un'occhiata al database:
Nuove tabelle di provider di appartenenze compaiono accanto a quelle vecchie
Tutte le tabelle sono evidenziati i nuovi, proprio come abbiamo visto in precedenza quando si inizia con il modello 2012 da zero. Allora è così - hashing di oggi (domani è se si considera che siamo ancora in RC) è facilmente raggiungibile in VS2010.

Gli hash non è hash

Aggiornamento di un 2010 modello di progetto è facile - e potenzialmente catastrofico. Il problema è semplicemente che se si dispone di un esistente progetto con esistenti hash, seguendo il consiglio di cui sopra fondamentalmente rompere l'autenticazione. Anche se la migrazione di tutti i conti delle tabelle vecchio al nuovo (che è abbastanza facile), gli hash memorizzati dei vecchi tempi non corrisponderanno alle nuove hash dal modello PBKDF2.Nessuno sarà in grado di accedere.
Ora, naturalmente, ci sono modi per codificare la tua via d'uscita da questo, è solo bisogno di sapere quale algoritmo di hash è stato utilizzato per ogni utente. Se conosci questa si può sempre l'autenticazione con il vecchio algoritmo poi una volta verificato, hash la password con il nuovo algoritmo. Ma il provider di appartenenze non ti dà questa possibilità, ne avrete bisogno per iniziare tirandolo a parte e attuare la propria logica.
L'altra opzione è che si può semplicemente rotolare verso il nuovo modello quindi chiedere agli utenti esistenti di reimpostare le password. Si ha una sicura funzione di reimpostazione della password , non è vero? In ogni caso, questo non sarà possibile in molti casi, ma se c'è un piccolo gruppo di utenti che si è in grado di costringere a compiere un processo un po 'scomoda, che è una facile via d'uscita.
Se si desidera ripristinare su un sistema esistente, ecco quello che vi serve per i provider di appartenenze e il ruolo (questo non riguarda il provider di profili):
INSERT INTO dbo . Applicazioni SELEZIONA ApplicationName , ApplicationId , 
Descrizione FROM dbo . aspnet_Applications
 GO

INSERT INTO dbo . Ruoli SELEZIONA ApplicationId , ID ruolo , RoleName , Descrizione 
FROM dbo . aspnet_Roles
 GO

INSERT INTO dbo . utenti SELEZIONA ApplicationId , UserId , UserName , IsAnonymous ,
 LastActivityDate FROM dbo . aspnet_Users
 GO

INSERT






INSERT INTO dbo . UsersInRoles SELEZIONA * FROM dbo . aspnet_UsersInRoles
 GO
Ma ancora una volta, ricordo, nessuno sarà in grado di accedere fino a quando non esegue un reset della password.

Un non-nativo alternativa

Nel post precedente ho dato una menzione resoconto di biblioteca Zetetic, che si integra con il provider di appartenenze e supporta bcrypt e PBKDF2 . Mi è piaciuta la facilità di tirare da NuGet ma lamenta il fatto che ha richiesto l'installazione e la modifica GAC machine.config, che ha escluso di correre sulle moderne implementazioni stile PaaS come sto utilizzando in AppHarbor o che troverete in Azure.
Per fortuna la gente oltre a Zetetic sono riusciti a riunire tutto ciò che a livello di computer dipendenza su in una soluzione piuttosto veloce, infatti è ora possibile utilizzare la propria libreria per implementare l'hashing delle password sicure per ASP.NET in una sola riga . Quella riga aggiunge semplicemente l'applicazione di mapping Zetetic algoritmo della app in caso Application_Start dei Global.asax.cs:
CryptoConfig . AddAlgorithm (   typeof (Zetetic.Security. Pbkdf2Hash ), "pbkdf2_local" );
Un modo che l'attuazione Zetetic differisce da quella del supporto web da Microsoft è che invece di 1000 iterazioni di un hash, stiamo guardando a 5.000. Dal punto di vista della forza bruta che stiamo letteralmente guardando cinque volte il carico di lavoro. Mentre vi è forse un sovraccarico di usabilità e le infrastrutture implicazione a questo, migliora certamente la sicurezza.
In realtà, ho specificamente testare la durata del metodo HashPassword nelle helper web e costantemente ottenuto risultati di circa 17ms. Ho anche testato specificamente tempi per una scala hash SHA1 e ho visto i risultati che variano da 1/1.000 a 1/3000th di ciò HashPassword sta facendo, in cui è all'interno della gamma di quello che ti aspetteresti (ricordiamo che classe Stopwatch affidabilità cosa, in particolare per tempi molto piccoli).Per l'hashing delle password, Sarei felice di fare un aumento di cinque volte su ciò che sta facendo HashPassword in, un posto più vicino a 100ms sarebbe preferibile per la maggior parte dei casi in applicazioni web e anche se GPU sfornerà attraverso di loro molto più veloce che mette solo le screpolature che fuori molto più in là della portata senza imporre quello che avevo considero una latenza inaccettabile per l'accesso.

Conclusioni

In entrambi i provider di appartenenze e la biblioteca universale Zetetic, sicuramente il mondo perfetto sarebbe quello di sfruttare la possibilità di personalizzare il carico di lavoro di hash? Come ho discusso nel post precedente, tutti hanno bisogno per trovare il loro posto proprio dolce tra impatto positivo la sicurezza e negativo impatto sulle prestazioni. Zetetic cita forse incapsulare questo all'interno di un'altra libreria e siamo ancora sentito niente da Microsoft sulla loro attuazione.
Ma a prescindere messa a punto del carico di lavoro, resta il fatto che dal punto di vista della sicurezza il nuovo provider partecipazione universale ci dà una mille volte miglioramento rispetto al vecchio. Nel libro di nessuno, questa è una cosa davvero molto buono.
Il lato negativo, però, in realtà si sta solo andando a utilizzare questo in nuove applicazioni e tutto ciò che hai già costruito con il fornitore originale rimane, francamente, poco meglio che se non memorizzazione di dati crittografici esisteva affatto. Poi ci sono quegli otto query SQL, non è buono, ma un problema noto ed essendo fissato dopo di che può essere tirato giù da NuGet con il minimo sforzo a tutti (non dimenticate di guardare per questo se si sta già utilizzando il nuovo fornitori).
Personalmente, mi sto dando un pollice in alto, tanto che sto tirando subito il vecchio modello di ASafaWeb , la migrazione dei conti chiedendo poi la gente a eseguire una reimpostazione della password. Ho il lusso di un piccolo pubblico di private beta tester che usano le caratteristiche di account esistenti e posso permettermi di metterli attraverso un po 'di disagio. In questo modo ora prima di raccogliere un maggior numero di account utente in app (cosa che scriverò molto presto), secondo la mia opinione è un investimento intelligente.
Quindi, in conclusione, è una buona cosa. Vai a prendere il fornitore universale e se è troppo difficile passare dal modello di appartenenza esistente, prego però ascolteranno che i vostri hash non vengono divulgati perché c'è molto poco di loro tutela se lo fanno!

Nessun commento:

Posta un commento