代码之家  ›  专栏  ›  技术社区  ›  sebastien

客户端/服务器应用程序的哈希用户密码

  •  6
  • sebastien  · 技术社区  · 13 年前

    我有一个与网络服务器通信的iPhone应用程序。启动应用程序时,用户必须使用用户名和密码进行身份验证。 应用程序和网络服务器之间的通信是安全的(HTTPS),但我不想向网络服务器发送清除密码,我只想发送密码的“签名”(签名将存储在网络服务器的数据库中)。

    在IOS上创建此“签名”的最佳解决方案是什么?我应该使用MD5还是其他什么?

    我需要获得一个外部库来构建这个签名吗?或者可以使用IOS SDK中的SecKeyEncrypt来完成吗?

    从长远来看,我将有一个在Android上运行的应用程序,我必须能够为IOS和Android生成相同的签名。

    谢谢你的帮助,

    斯巴斯蒂安。

    2 回复  |  直到 13 年前
        1
  •  20
  •   Holly    13 年前

    以明文形式传输密码是不好的,所以做任何事情都是好的第一步。如果你要努力,那么知道如何做对是值得的。

    虽然MD5不再是一种强大的哈希算法,但在MD5和SHA256(甚至SHA512)之间进行选择不如如何使用它重要。让我们忽略哈希算法的细节,先看看如何使用它。

    使用散列的想法是,字符串的散列总是相同的,并且是单向操作。通过捕获字符串,不可能(或不实用)确定密码。近年来,随着彩虹表的大量使用,这种情况变得不真实了。彩虹表包含所有可能的密码(长度不超过给定长度)及其哈希,因此攻击者可以使用反向查找来发现密码。Rainbow表可用于16个字符以下密码的所有哈希算法。

    这个问题有几种常见的解决方案。一种是多次(大约1000次)执行散列。客户端和服务器都必须知道并预先确定确切的次数,这样他们才能执行相同的操作。这具有使哈希生成变得昂贵的优点和缺点。攻击者在计算上更难使用暴力,但如果彩虹表扩展到足够大,它们仍然很有用。

    一个更好但不太常见的解决方案是在密码中添加一个已知的随机字符串(通常称为Salt),使其变长(可能是64个字符)。客户端和服务器都必须事先知道这个salt。这种解决方案既便宜又简单,即使盐漏出来也无关紧要。

    密码哈希还有另一个常见问题。如果恶意用户知道用户密码的散列,那么对于设计糟糕的系统来说,这就像知道密码本身一样好。让我们假设我们有一个RPC函数,它需要用户名和密码散列。知道密码散列的恶意用户可以提交它,即使不知道密码,也可以访问系统。这个已知的密码散列将继续工作,直到用户更改密码,可能需要数月或数年。所需要的是一种限制密码哈希有用的持续时间的方法。这是通过使用动态盐来实现的。

    然后,身份验证就变成了一个多步骤的过程。

    1. 客户端连接到服务器并呈现某种客户端(或设备)标识符,例如UUID。
    2. 然后,服务器为该客户端标识符生成一个动态salt。动态盐只在短时间内(通常为几分钟到几小时)是好的。
    3. 服务器将动态salt、它的过期时间和相关联的客户端标识符存储在数据库表中以供将来使用。
    4. 服务器将动态salt连同其过期时间一起返回给客户端。
    5. 客户端使用上面两种机制中的任何一种对密码进行散列,连接动态salt,再次散列,然后尝试使用用户名、客户端标识符和动态salt散列进行身份验证。
    6. 服务器通过将用户已知的密码哈希与提交的值进行检查来验证提交的凭据,试图将该客户端标识符的每个已知动态salt连接并哈希。如果找到匹配项,则接受身份验证。

    这(大致)是MySQL使用的机制。它足够安全,可以在没有SSL的情况下安全使用,但我始终建议使用SSL,以便保护其余的有效负载。

    如果您使用这样的安全机制,那么使用MD5或SHA变体也没什么关系。也就是说,除非有充分的理由表明MD5是必要的,否则任何新的开发都不使用SHA256是没有意义的。

        2
  •  1
  •   Lukasz 'Severiaan' Grela    12 年前

    md5不是最好的主意——现在有很多非常快速的彩虹表可以解密它们。

    我建议您在iOS上使用AES256 NSData+CommonCrypto 这让你可以很容易地做到这一点。

    NSString* encryptionPass = @"myEncryptionPass";
    NSData* passData = [userPassword dataUsingEncoding:NSStringEncodingConversionAllowLossy];
    NSError* error = nil;
    NSData* encryptedPassData = [passData AES256EncryptedDataUsingKey:encryptionPass error:&error];
    if(!error)
    {
         NSString* encryptedString = [[NSString alloc] initWithData:encryptedPassData encoding:NSUTF8StringEncoding]
    }