【问题标题】:Generating a strong password in C#?在 C# 中生成强密码?
【发布时间】:2011-01-17 00:38:05
【问题描述】:

我想知道如何在 C# 中生成强而安全的密码。

我google了一下,在维基百科看到了这个公式,其中L是密码的长度,N是可能的符号数:

另外,我找到了this question,但由于某种原因,Membership.GeneratePassword 方法只返回一个 1 位随机数,绝对没有密码。所有其他解决方案都非常慢(>= 0.5 秒)。

我需要帮助来实现这个公式(我不知道从哪里开始)。您还可以提出其他解决方案或解释为什么 GeneratePassword 不起作用。

【问题讨论】:

    标签: c# .net passwords


    【解决方案1】:

    为什么不只是用一些字符填充一个数组并随机选择其中的一些。您可以将它们分组,以确保包含字母数字和特殊字符。

    您还必须选择合适的长度以及要包含的每组字符的数量,仅此而已。我认为您不需要一些复杂的公式。

    【讨论】:

    • 没什么...但是如果您将登录限制为每小时 5 次登录,则表示您降低了暴力攻击的风险。
    • 还有什么比选择随机字符更难的呢?
    • @anthares 我同意,Alon 这是干什么用的?
    • 不,我不会限制我每小时的登录次数,这对用户来说非常恼火。相反,我将在 3 次尝试后输入 CAPTCHA。
    • @Alon 该方法将以相同的方式工作。所以英军攻击受到了限制。
    【解决方案2】:

    实际上,对于不允许用户生成密码的系统来说,这很容易:任何密码都尽可能安全。当然,不包括在显示器上贴便利贴等的人。

    您可能希望最大化生成密码的字符集。但是限制生成的密码会大大减少搜索空间,从而使密码 less 安全。同样,这仅在用户无法选择自己的密码时才成立。

    如果您同时处理生成的密码和用户创建的密码,那么显然所有的赌注都没有了。然后,您可能希望以一种使用尽可能多的来自不同类别的字符的方式生成密码,类似于用户选择的强密码。理想情况下,它应该符合用户创建的密码也必须通过的相同约束(如果有的话)。

    【讨论】:

      【解决方案3】:

      我刚刚在 linqpad 中尝试了以下操作:

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

      这是我得到的密码:

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

      或者,如果这还不够安全,试试这个:

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

      运行 3 次后得到以下结果:

      |c^.:?m)#q+(]V;}[Z(})/?-;$]+@!|^/8*_9.$&.&!(?=^!Wx ?[@%+&-@b;)>N;&+*w[>$2+_$%l;+h+#zhs^{e?&=*(}X_%|:}]]}*X [+)Er%J/-=;Q0{:+=%c7:^$
      
      /:_)hxF+*){2|;(>:*N^+!_&|}B.$})?[V=[+v({-:-@9-Z$j?.[-} (@MHx+}(}Mz_S(7#4}{..>@G|!+++{+C=|_}=+r^@&$0;L*|kz-;$++/N3$ =}?;%&]]*/^#^!+
      
      :*{]-x^$g{|?*))_=B@^.#%L;g|+)#[nq}?y(_(m;]S^I$*q=l- [_/?}&-!k^(+[_{Z|&:^%!_)!=p%=)=wYd-#.UP$%s1{*l%+[%?!c+ 7=@=.;{+M)!^}&d/]{];(&}

      这不到一秒钟,顺便说一句。框架是你的朋友。

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

      【讨论】:

      • @alon 你做错了。相信我。此方法适用于任何计算机上的任何 .NET 安装,并且是防弹的。
      • @Will - 我不会把“你做错了”作为一个答案。 :)
      • @CRice 我认为Membership.GeneratePassword 是基于System.Security.Cryptography.RNGCryptoServiceProvider,所以你可以用它来实现你自己的 GeneratePassword。这是一个例子:obviex.com/Samples/Password.aspx
      • 虽然这在数学意义上很强大 - 它完全忽略了人为因素。这种格式的任何密码都必须写下或存储,这会引发更多问题。最好使用密码短语“HelpMeObiWanKanobieYou'reMyOnlyHope” ...
      • “此方法在任何 .NET 安装中都有效并且是防弹的”除了该程序集不适用于 .NET Core 并且不是 .NET Standard 的一部分..
      【解决方案4】:

      解决您关于该公式的问题:

      公式是说,从 N 个符号的字母表中提取的长度为 L 的密码等价于从 两个 符号的字母表中提取的长度为 H 的密码。因此,如果您有 64 个符号(例如 abc...xyzABC...XYZ01...89_!)并且密码长度为 10 个字符,那么这为您提供与密码 10 log2 64 = 60 个字符长度相同的安全性取自字母“ab”。

      “log”是求幂的逆运算。二的六次方给你六十四,因此六十四的“对数二”给你六。

      【讨论】:

        【解决方案5】:

        我自己会使用 Guid :),并让用户在登录后编辑密码

        【讨论】:

          【解决方案6】:

          我不知道这是否会对您有所帮助,但是当我想生成一个随机密码时,我会使用它,它也很强大。它的实施/理解既快速又简单,并且不像通过上面的会员提供者那样过度杀戮......

              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);
              }
          

          【讨论】:

          • System.Random 永远不应该用于生成更可预测的密码,并且不用于此目的。请改用System.Security.Cryptography.RNGCryptoServiceProvider 参见stackoverflow.com/a/411985/1160036stackoverflow.com/a/8996788/1160036
          • 我不建议自己滚动进行安全设计。使用像 .net 这样的内置安全框架将为您提供经过密码学专家全面测试的代码。一个人很容易犯很多错误。
          【解决方案7】:

          不确定我在哪里找到的,但这是一个生成高熵、真正随机字符串的类,可用作密码。

          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 = "!@#%*()$?+-=";
          
              }
          
              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);
              }
          }
          

          因此在您的代码中使用:

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

          【讨论】:

          • 这需要一点点按摩,但这是一个坚实的开始。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-09-20
          • 1970-01-01
          • 2021-07-01
          • 2020-12-12
          • 1970-01-01
          • 2017-06-11
          • 1970-01-01
          相关资源
          最近更新 更多