2010-02-15 15 views
27

Mi chiedevo come posso generare una password sicura e sicura in C#.Generazione di una password complessa in C#?

Googled un po 'e ho visto questa formula in Wikipedia, dove L è la lunghezza della password e N è il numero di possibili simboli:

alt text

Inoltre, ho trovato this question, ma per qualche ragione il metodo Membership.GeneratePassword restituisce solo un numero casuale con 1 cifra, che assolutamente nessuna password. Tutte le altre soluzioni erano molto lente (> = 0,5 secondi).

Ho bisogno di aiuto per l'implementazione di questa formula (non so da dove iniziare). Puoi anche suggerire un'altra soluzione o spiegare perché il GeneratePassword non funziona.

risposta

73

Ho appena provato quanto segue in LINQPad:

System.Web.Security.Membership.GeneratePassword(25, 10) 

Questa è la password che ho ottenuto:

[XTJ_67g.i/ag1rL)6_Yv>*+%

Oppure, se questo non è abbastanza sicuro, provate questo:

System.Web.Security.Membership.GeneratePassword(128, 100) 

che mi ha ottenuto quanto segue quando lo si esegue tre volte:

|c^.:?m)#q+(]V;}[Z(})/?-;$][email protected]!|^/8*_9.$&.&!(?=^!Wx?[@%[email protected];)>N;&+*w[>$2+_$%l;+h+#zhs^{e?&=*(}X_%|:}]]}*X[+)Er%J/-=;Q0{:+=%c7:^$ 

/:_)hxF+*){2|;(>:*N^+!_&|}B.$})?[V=[+v({-:[email protected]$j?.[-}(@MHx+}(}Mz_S(7#4}{..>@G|!+++{+C=|_}=+r^@&$0;L*|kz-;$++/N3$=}?;%&]]*/^#^!+ 

:*{]-x^$g{|?*))[email protected]^.#%L;g|+)#[nq}?y(_(m;]S^I$*q=l-[_/?}&-!k^(+[_{Z|&:^%!_)!=p%=)=wYd-#.UP$%s1{*l%+[%[email protected]=.;{+M)!^}&d/]{];(&}

questo è durato meno di un secondo, btw. Il quadro è tuo amico.

Vedi http://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword.aspx

+0

Si prega di notare l'editor non gioca amichevole con alcuni dei caratteri nella password. – Will

+0

Come ho detto, questo è un ottimo metodo, ma restituisce solo una cifra nel mio computer e in molti altri computer. –

+4

@alon stai sbagliando. Fidati di me. Questo metodo funziona ed è a prova di proiettile su qualsiasi installazione .NET su qualsiasi computer. – Will

1

Perché non riempire semplicemente un array con alcuni caratteri e selezionarne a caso un numero. Puoi dividerli in gruppi per assicurarti che includano numeri di lettere e caratteri speciali.

Dovrai anche scegliere una lunghezza adeguata e quanto di ogni gruppo di caratteri includere e il gioco è fatto. Non penso che tu abbia bisogno di alcune formule sofisticate.

+0

non credo che sia abbastanza sicuro per attacchi di forza bruta ed ecc ... –

+1

Niente è ... Ma se si limita i dati di accesso a 5 gli accessi all'ora dicono che riduci il rischio di un attacco di forza bruta. – LiamB

+2

Cosa potrebbe essere più difficile del selezionare caratteri casuali? – anthares

1

Per i sistemi che non consentono le password generati dagli utenti è molto facile, in realtà: Qualsiasi password è sicuro quanto è lungo. Senza contare, naturalmente, le persone che attaccano i post-it ai monitor, ecc.

Probabilmente si desidera massimizzare il set di caratteri da cui viene generata la password. Ma limitare le password generate riduce notevolmente lo spazio di ricerca e pertanto rende la password in meno in meno. Di nuovo, ciò vale solo se l'utente non può scegliere la propria password.

Se si gestiscono le password generate e create dall'utente, tutte le scommesse sono disattivate, ovviamente. Probabilmente vorrai generare le password in modo che utilizzi il maggior numero possibile di caratteri di classi diverse, simile a una forte password scelta dall'utente. Idealmente dovrebbe essere conforme agli stessi vincoli che la password creata dall'utente deve passare (se esiste).

2

Come su Guid.NewGuid().ToString();?

+4

Siamo spiacenti, questo ritornerà 00000000-0000-0000-0000-000000000000 tutto il tempo. Ma questo funzionerà: Guid.NewGuid(). ToString() – Steven

+0

@Steven - Modificato secondo il suggerimento – Nifle

+0

ehm ... sì ... questo è quello che intendevo .... oO – dkackman

9

Per affrontare le tue domande su quella formula:

La formula sta dicendo che una password di lunghezza L tratto da un alfabeto di N simboli equivale a una password di lunghezza H tratto da un alfabeto di due simboli . Quindi, se hai, diciamo, 64 simboli (ad esempio abc ... xyzABC ... XYZ01 ... 89_!) E la password è lunga 10 caratteri, quindi ti dà sicurezza equivalente a una password 10 log2 64 = 60 caratteri lunghi tratto dall'alfabeto "ab".

Un "registro" è l'operazione inversa di elevazione a potenza. Due alla sesta potenza ti danno sessantaquattro, quindi il "log due" di sessantaquattro ti dà sei.

1

userei Guid me :), e fare all'utente di modificare la password dopo il login

6

non so se questo vi aiuterà, ma questo è quello che uso quando voglio generare un casuale password che è anche forte. E 'veloce e semplice da implementare/capire e non è tanto di un peso inutile come quello attraverso il provider di appartenenze sopra ...

private string Token(byte Length) { 
     char[] Chars = new char[] { 
      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 
     }; 
     string String = string.Empty; 
     Random Random = new Random(); 

     for (byte a = 0; a < Length; a++) { 
      String += Chars[Random.Next(0, 61)]; 
     }; 

     return (String); 
    } 
+6

'System.Random' non dovrebbe mai essere utilizzato per generare password più prevedibile e non è destinato a tale scopo. Usa invece 'System.Security.Cryptography.RNGCryptoServiceProvider' Vedi http://stackoverflow.com/a/411985/1160036 e http://stackoverflow.com/a/8996788/1160036 – Despertar

+1

Non ti consiglierei di roll-your-own per design di sicurezza. L'utilizzo di un framework di sicurezza integrato come .net ti fornirà un codice testato accuratamente da esperti di cryptogrophy. È facile fare molti errori da solo. –

8

Non so dove ho trovato questo, ma qui è una classe per generare alta entropia, stringhe veramente casuali che possono essere utilizzate come password.

using System.Security.Cryptography; 
using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 

public class PasswordGenerator 
{ 
    public int MinimumLengthPassword { get; private set; } 
    public int MaximumLengthPassword { get; private set; } 
    public int MinimumLowerCaseChars { get; private set; } 
    public int MinimumUpperCaseChars { get; private set; } 
    public int MinimumNumericChars { get; private set; } 
    public int MinimumSpecialChars { get; private set; } 

    public static string AllLowerCaseChars { get; private set; } 
    public static string AllUpperCaseChars { get; private set; } 
    public static string AllNumericChars { get; private set; } 
    public static string AllSpecialChars { get; private set; } 
    private readonly string _allAvailableChars; 

    private readonly RandomSecureVersion _randomSecure = new RandomSecureVersion(); 
    private int _minimumNumberOfChars; 

    static PasswordGenerator() 
    { 
     // Define characters that are valid and reject ambiguous characters such as ilo, IO and 1 or 0 
     AllLowerCaseChars = GetCharRange('a', 'z', exclusiveChars: "ilo"); 
     AllUpperCaseChars = GetCharRange('A', 'Z', exclusiveChars: "IO"); 
     AllNumericChars = GetCharRange('2', '9'); 
     AllSpecialChars = "[email protected]#%*()$?+-="; 

    } 

    public PasswordGenerator(
     int minimumLengthPassword = 15, 
     int maximumLengthPassword = 20, 
     int minimumLowerCaseChars = 2, 
     int minimumUpperCaseChars = 2, 
     int minimumNumericChars = 2, 
     int minimumSpecialChars = 2) 
    { 
     if (minimumLengthPassword < 15) 
     { 
      throw new ArgumentException("The minimumlength is smaller than 15.", 
       "minimumLengthPassword"); 
     } 

     if (minimumLengthPassword > maximumLengthPassword) 
     { 
      throw new ArgumentException("The minimumLength is bigger than the maximum length.", 
       "minimumLengthPassword"); 
     } 

     if (minimumLowerCaseChars < 2) 
     { 
      throw new ArgumentException("The minimumLowerCase is smaller than 2.", 
       "minimumLowerCaseChars"); 
     } 

     if (minimumUpperCaseChars < 2) 
     { 
      throw new ArgumentException("The minimumUpperCase is smaller than 2.", 
       "minimumUpperCaseChars"); 
     } 

     if (minimumNumericChars < 2) 
     { 
      throw new ArgumentException("The minimumNumeric is smaller than 2.", 
       "minimumNumericChars"); 
     } 

     if (minimumSpecialChars < 2) 
     { 
      throw new ArgumentException("The minimumSpecial is smaller than 2.", 
       "minimumSpecialChars"); 
     } 

     _minimumNumberOfChars = minimumLowerCaseChars + minimumUpperCaseChars + 
           minimumNumericChars + minimumSpecialChars; 

     if (minimumLengthPassword < _minimumNumberOfChars) 
     { 
      throw new ArgumentException(
       "The minimum length of the password is smaller than the sum " + 
       "of the minimum characters of all catagories.", 
       "maximumLengthPassword"); 
     } 

     MinimumLengthPassword = minimumLengthPassword; 
     MaximumLengthPassword = maximumLengthPassword; 

     MinimumLowerCaseChars = minimumLowerCaseChars; 
     MinimumUpperCaseChars = minimumUpperCaseChars; 
     MinimumNumericChars = minimumNumericChars; 
     MinimumSpecialChars = minimumSpecialChars; 

     _allAvailableChars = 
      OnlyIfOneCharIsRequired(minimumLowerCaseChars, AllLowerCaseChars) + 
      OnlyIfOneCharIsRequired(minimumUpperCaseChars, AllUpperCaseChars) + 
      OnlyIfOneCharIsRequired(minimumNumericChars, AllNumericChars) + 
      OnlyIfOneCharIsRequired(minimumSpecialChars, AllSpecialChars); 
    } 

    private string OnlyIfOneCharIsRequired(int minimum, string allChars) 
    { 
     return minimum > 0 || _minimumNumberOfChars == 0 ? allChars : string.Empty; 
    } 

    public string Generate() 
    { 
     var lengthOfPassword = _randomSecure.Next(MinimumLengthPassword, MaximumLengthPassword); 

     // Get the required number of characters of each catagory and 
     // add random charactes of all catagories 
     var minimumChars = GetRandomString(AllLowerCaseChars, MinimumLowerCaseChars) + 
         GetRandomString(AllUpperCaseChars, MinimumUpperCaseChars) + 
         GetRandomString(AllNumericChars, MinimumNumericChars) + 
         GetRandomString(AllSpecialChars, MinimumSpecialChars); 
     var rest = GetRandomString(_allAvailableChars, lengthOfPassword - minimumChars.Length); 
     var unshuffeledResult = minimumChars + rest; 

     // Shuffle the result so the order of the characters are unpredictable 
     var result = unshuffeledResult.ShuffleTextSecure(); 
     return result; 
    } 

    private string GetRandomString(string possibleChars, int lenght) 
    { 
     var result = string.Empty; 
     for (var position = 0; position < lenght; position++) 
     { 
      var index = _randomSecure.Next(possibleChars.Length); 
      result += possibleChars[index]; 
     } 
     return result; 
    } 

    private static string GetCharRange(char minimum, char maximum, string exclusiveChars = "") 
    { 
     var result = string.Empty; 
     for (char value = minimum; value <= maximum; value++) 
     { 
      result += value; 
     } 
     if (!string.IsNullOrEmpty(exclusiveChars)) 
     { 
      var inclusiveChars = result.Except(exclusiveChars).ToArray(); 
      result = new string(inclusiveChars); 
     } 
     return result; 
    } 
} 

internal static class Extensions 
{ 
    private static readonly Lazy<RandomSecureVersion> RandomSecure = 
     new Lazy<RandomSecureVersion>(() => new RandomSecureVersion()); 
    public static IEnumerable<T> ShuffleSecure<T>(this IEnumerable<T> source) 
    { 
     var sourceArray = source.ToArray(); 
     for (int counter = 0; counter < sourceArray.Length; counter++) 
     { 
      int randomIndex = RandomSecure.Value.Next(counter, sourceArray.Length); 
      yield return sourceArray[randomIndex]; 

      sourceArray[randomIndex] = sourceArray[counter]; 
     } 
    } 

    public static string ShuffleTextSecure(this string source) 
    { 
     var shuffeldChars = source.ShuffleSecure().ToArray(); 
     return new string(shuffeldChars); 
    } 
} 

internal class RandomSecureVersion 
{ 
    //Never ever ever never use Random() in the generation of anything that requires true security/randomness 
    //and high entropy or I will hunt you down with a pitchfork!! Only RNGCryptoServiceProvider() is safe. 
    private readonly RNGCryptoServiceProvider _rngProvider = new RNGCryptoServiceProvider(); 

    public int Next() 
    { 
     var randomBuffer = new byte[4]; 
     _rngProvider.GetBytes(randomBuffer); 
     var result = BitConverter.ToInt32(randomBuffer, 0); 
     return result; 
    } 

    public int Next(int maximumValue) 
    { 
     // Do not use Next() % maximumValue because the distribution is not OK 
     return Next(0, maximumValue); 
    } 

    public int Next(int minimumValue, int maximumValue) 
    { 
     var seed = Next(); 

     // Generate uniformly distributed random integers within a given range. 
     return new Random(seed).Next(minimumValue, maximumValue); 
    } 
} 

Consumare nel codice seguente modo:

var generator = new PasswordGenerator(); 
string password = generator.Generate(); 
Console.WriteLine(password); 
+0

Questo ha richiesto un po 'di massaggio, ma è un buon inizio. – im1dermike

+0

Grazie per questo, ottimo lavoro. – fkerrigan

+0

Questo è veramente buono. grazie! – MovGP0