【问题标题】:Password Verification - How to securely check if entered password is correct密码验证 - 如何安全地检查输入的密码是否正确
【发布时间】:2014-04-29 02:40:47
【问题描述】:

我正在开发一个需要多个密码才能访问不同数据区域的应用。例如,一群人可以建立一个需要密码验证才能查看的聊天。

我正在考虑这样做:

我有我的关键字,假设性地说:

香蕉

当用户输入他们的密码时,我使用 RNCryptor 使用他们输入的密钥加密Banana,并将该加密字符串存储到服务器。

稍后,当有人尝试输入密码时,我从服务器获取哈希值并尝试使用他们输入的密码作为密钥对其进行解密。如果解密后的值等于Banana,我知道他们输入了正确的密码。

我是安全新手,所以我不确定这是否是一个合适的解决方案。感谢所有帮助。

更新

在对@Greg 和恰当命名为@Anti-weakpasswords 的建议进行了一些更改后,这就是我所拥有的:

- (NSDictionary *) getPasswordDictionaryForPassword:(NSString *)password {

    NSData * salt = [self generateSalt256];
    NSData * key = [RNCryptor keyForPassword:password salt:salt settings:mySettings];

    NSMutableDictionary * passwordDictionary = [NSMutableDictionary new];

    NSString * saltString = stringFromData(salt);
    NSString * keyString = stringFromData(key);

    passwordDictionary[@"key"] = keyString;
    passwordDictionary[@"salt"] = saltString;
    passwordDictionary[@"version"] = @"1.0.0";
    passwordDictionary[@"iterationCount"] = @"10000";

    return passwordDictionary;
}

static const RNCryptorKeyDerivationSettings mySettings = {
    .keySize = kCCKeySizeAES256,
    .saltSize = 32,
    .PBKDFAlgorithm = kCCPBKDF2,
    .PRF = kCCPRFHmacAlgSHA1,
    .rounds = 10000
};

- (NSData *)generateSalt256 {
    unsigned char salt[32];
    for (int i=0; i<32; i++) {
        salt[i] = (unsigned char)arc4random();
    }
    NSData * dataSalt = [NSData dataWithBytes:salt length:sizeof(salt)];
    return dataSalt;
}

【问题讨论】:

  • 您的解决方案看起来不错,但恕我直言,更好的方法是将哈希存储在服务器上并从客户端发送每个猜测密码以进行检查

标签: objective-c security encryption passwords rncryptor


【解决方案1】:
  • 不要使用任何散列函数的单次传递来存储密码。
  • 请务必使用 8-16 字节范围内的随机盐。
  • 不要使用可逆加密来存储密码。
  • 请勿使用与您输入的密码完全相同的密码作为加密密钥。

相反,当用户选择关键字/密码时

  • 生成一个加密随机的 8-16 字节盐
  • 将 PBKDF2、BCrypt 或 SCrypt 与所述盐和尽可能大的迭代计数/工作因子一起使用,以创建密码哈希
    • 如果您特别使用 PBKDF2,请不要请求大于本机哈希大小的输出(SHA-1 = 20 字节,SHA-256 是 32 字节,SHA-384 是 48 字节,SHA-512 是 64 字节),或者您增加了攻击者相对于您(防御者)的比较优势。

然后在您的数据库中,存储该用户的特定信息:

  • 清盐
  • 迭代次数/工作系数
    • 因此您以后可以轻松更改/升级它
  • 生成的密码哈希
  • 身份验证协议的版本 - 这可能是 2,也可能是 1。
    • 因此,如果您稍后从该方法转移到 NewWellKnownMethod,则可以轻松更改/升级它

当用户想要对您的系统进行身份验证时,您:

  • 从数据库中检索他们的版本、盐、迭代计数/工作因子以及生成的哈希
  • 使用数据库中的盐和迭代计数/工作因子对他们刚刚输入的任何关键字/密码进行哈希处理。
  • 将刚刚得到的结果与数据库中的结果进行比较;如果他们是一样的,让他们进来。
    • 高级:使用恒定时间比较,因此如果第一个字节不同,它不会直接退出尝试,以减少对定时攻击的脆弱性。

请阅读How to securely hash passwords?,其中 Thomas Porrin 的回答是目前最常被提及的 Stackexchange 关于密码散列的论文,当然也是迄今为止我见过的最好的。

【讨论】:

  • 非常感谢您的回答,您介意简要检查一下我是如何重新实现它的吗?在我的示例中,盐应该是 8 - 16 而不是 32?
  • 更长的盐不是问题;这不是一个好处。尝试一些基准测试,并选择多少次迭代花费您愿意花费的时间(尽管 10000 是 SHA-1 的合理硬最小值)。假设您在 64 位系统上,更改为 kCCPRFHmacAlgSHA384 或 kCCPRFHmacAlgSHA512 将降低 2014 年老式 GPU 相对于您的 CPU 的相对优势,因为 64 位操作在当前一代 GPU 上表现不佳。您可能还想检查 arc4random();至少,FreeBSD 在启动后曾经遇到过熵问题 (SA-08.11)。
  • 请注意,您可以在 Code Review Stackexchange 网站上获得更全面的一般审查。
  • 我将尽可能提高迭代次数。我不确定如何替换 arc4random(),但我会看看我发现了什么。感谢您解释其中的一些内容!
  • 优秀的迭代提升!我不会说替换 arc4random(),但我会说检查一下你的特定系统是否有任何重大缺陷。
【解决方案2】:

这样做不是很好的方法。您应该使用一种方法散列算法来散列密码(您将无法解密它)。 在您对密码进行哈希处理后,在用户提供密码后将其保存到数据库中,您必须对其进行哈希处理并将其(哈希值)与您存储在数据库中的哈希值进行比较。如果匹配则表示认证失败则相同。

这样,即使有人可以访问数据库,数据也会受到保护,他不会对此做任何事情,那里存储的不是密码。

大部分身份验证都是通过这种方式进行的。

//扩展

你应该使用一些为这种工作而制定的哈希算法。

查看SHAMD5

【讨论】:

  • 我注意到使用 RNCryptor 我偶尔会得到相同值的不同哈希值。如果不生成相同的哈希,这不会导致并发症吗?
  • @Greg SHA 或 MD5 在我们这个时代并不是真正的加密
  • @sage444 抱歉,什么意思? SHA1 或 MD5 是一种散列函数,广泛用于存储密码。
  • 我的意思是那是老算法,而且有很多被破解的例子。但是你是对的,散列绝对是保存密码的正确方法
  • Greg & @Sage444 - 尽管我目前的方法不涉及在任何地方保存用户的实际密码,但这仍然是首选解决方案?
猜你喜欢
  • 2017-07-24
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-09
  • 2023-03-17
  • 2015-12-28
相关资源
最近更新 更多