4

我有一个人们可以注册的网站,我需要加密他们的密码。我已经对其进行了研究,但我找不到任何不会被现代 GPU 的强大功能击败的方法。

所以我来找 StackOverflow 的好人问,什么是最强大的加密方法,我已尽我所能阻止人们接触数据库,但我想尽可能确定他们的数据会是如果数据库以某种方式被盗,那很好。

我想知道的其他事情是,以某种方式随机化密码中的字符会更安全吗?

编辑: 我使用了 Andrew Moore 的 bcrypt 实现(How do you use bcrypt for hashing passwords in PHP?)并提出了这个:

public static function Encrypt($Str,$Salt)
{
    $bcrypt = new \bcrypt();
    return $bcrypt->hash(SERVER_SALT . md5($Str) . $Salt);
}

如果有人看到它有任何问题或任何弱点,请告诉我。

4

2 回答 2

18

对于密码,您无法击败bcrypt. 这是关于 SO 的链接:How do you use bcrypt for hashing passwords in PHP? . 它的主要优点是:它本质上很慢(而且安全)。虽然普通用户会使用一次,并且不会理解十分之一秒和百万分之一秒之间的差异,但破解者会发现,他现在需要12 个世纪而不是在四天内找到密码(除了强制 Blowfish每秒一百万次尝试不需要四天:除非实施错误和尚未预见到的加密突破,否则宇宙的热寂仍将首先出现)。

对于数据,我会依赖数据库引擎本身;MySQL 支持 AES,这非常好。是的,只要有足够多的 GPU 就可以击败它,而且由于要在本世纪内破解代码需要大约 40 亿个 GPU,他可能也会得到一个不错的数量折扣。但我不会担心。

至于密码的“随机化”,没有任何意义。如果有人使用暴力破解您的密码,他将有相同的概率找到随机序列或非随机序列。如果要使用字典攻击可能会有所帮助(即,不要尝试从 AAAAA 到 ZZZZZ 的所有内容,而是尝试 ABRAHAM、ACCORDION ……直到 ZYGOTE)。但即便如此,对服务器的攻击也不会改变任何事情(服务器接收到非随机密码!),对存储哈希的攻击最好通过salting无效,这bcrypt自动执行:即,存储 $76XJ:MICKEYMOUSE 的散列而不是 MICKEYMOUSE 的散列,以及处理输入时额外的 '$76XJ:' 所需的开销。虽然必须修复随机化,但“$76XJ:”序列每次写入时都完全不同——祝你好运!

如果您自己实现加盐(如上所示,bcrypt您不需要),您可以通过生成唯一的随机序列并将其存储在密码字段或第二个字段中来实现。例如

user    password
alice   d9c06f88:6c14d6d313d7cbcb132b5c63650682c4

然后,在 Alice(“mickeymouse”)收到密码后,您将在数据库中查看是否alice存在被调用的用户。如果是,请恢复盐(此处d9c06f88)和哈希。如果没有,设置一个“BAD”标志并获取一个固定的盐和散列(例如12345678:0000000...)。

在 MySQL 中,这可以使用 UNION 来完成:

SELECT password_fields FROM users WHERE user=? AND hash=?
UNION SELECT
    '12345789' as salt,
    'ffffffffffffffffffffffff' as hash,
    'fake' as user
LIMIT 1;

将在大致相同的时间内检索正确的数据或不正确的集合(这可以防止有人通过计时回答“错误的用户或密码”所需的时间来猜测哪些名称存在,哪些不存在)。

然后将盐和密码连接起来,生成d9c06f88:mickeymouse. 如果不匹配,或者设置了“BAD”标志,则拒绝密码(而不是使用“bad”标志,您可以重复 MySQL 已经进行的用户匹配测试:您可以通过替换最后一个字符无效,以确保它永远不会匹配真实用户)。

通过非信息实现安全

选择固定字符串的附加扭曲很有用,因为您希望“用户不存在”、“用户存在但密码不正确”和“用户存在且密码正确”这三种情况相同(相同复杂性,相同计算费用)尽可能。

这样,攻击者就不太可能知道发生了什么:用户不正确吗?密码错了吗?等等。如果时间足够不同(比如两次查询有效用户,一次查询无效用户),一个小心翼翼、时间和好的秒表的攻击者可以统计确定给定用户名是否存在。

在用户的情况下,即使名称比较也会

 johnsmith     against   johnsmith            if johnsmith exists
 johnsmith     against   johnsmit?            if johnsmith does not exist

所以从 HTTP(s) 连接的另一端说出发生了什么并不容易。

出于同样的原因,您不会为“Bad user”和“Bad password”返回两个不同的错误,而始终是“Bad user or password”;对于用户来说,也许可以选择在他/她的注册电子邮件中接收电子邮件,以提醒他/她的用户名。当然,您希望系统在向同一用户发送类似电子邮件后的 24 小时内不发送此类电子邮件,以防止系统被利用以虚假的“恢复电子邮件”骚扰某人。

如果您确实发送了电子邮件,您将等到预设时间到期(例如 3 秒),然后通知用户如果存在用户名,那么他们应该检查他们的收件箱(以及垃圾邮件文件夹以防万一)。

这样的密码本来是有时间的

提高服务器安全性以防止暴力破解的一种便捷方法是延迟密码身份验证,并且可能(如果您真的很偏执)在某些 X 次错误尝试后进行验证码锁定。

您不希望第一次尝试验证码,因为用户对验证码的看法很模糊。

延迟锁定通常使用sleep(或等效)或使用“锁定直到管理员手动重置”策略来实现。两种方法都不好。锁定功能可用于创建拒绝服务攻击,方法是锁定用户,或创建大量以“密码验证延迟”状态停止的服务器线程(它们不会使用 CPU,但仍会使用内存,套接字和其他资源)。

在某些情况下,这可能会在不知不觉中发生。似乎有些白痴在使用我的银行,每隔几个月我就会收到一条短信,上面写着“在您的家庭银行系统中输入了错误的 PIN”。然后我必须从我的手机登录,以重置不成功尝试计数器;因为如果这个白痴没有意识到他因为输入了我的帐号而无法进入他的帐户,那么三次,的帐户就会被锁定,我必须亲自去银行请求他们重置我的帐户使用权。我告诉你,这是冥域的一痛点,即使知道这不是他们的错,我仍然对我的银行感到不满。你不想在你的用户中产生这样的感觉。

最好将负担转移到客户身上:

(very pseudo code)
login = FAIL
if in SECURITY LOCKOUT MODE for this account
    if a session is open and contains a last-attempt time
        if at least DELAY seconds have elapsed since last-attempt
            check the password
            if it is correct
                login = OK
                zero password counter, exit lockout mode.
                # An "innocent" user enters with no lockout! Yay!
            end
        else
            # "Early knocker". Either a bruteforcing robot
            # or a too clever user in a hurry. But we can't
            # tell them apart.
        end
    else
        # No session. Either a sessionless bruteforcing robot
        # or a unaware, innocent user. Again we can't tell them
        # apart. So we bounce both.

        # But a genuine user will assume he has mistyped the password,
        # or anyway will read the warning page, and will login after ONE
        # round of delay.

        # Users with password saved in browser will just click
        # "login" again and be logged in.

        # A robot will find itself delayed and ALL ITS PASSWORDS IGNORED
        # every single time. Even if it finds the right password... it will
        # not work.
    end
else
    check the password
    if it is correct
        # Good user, first attempt, fast login.
        login = OK
    else
        # Beginning to doubt this is a good user...
        increase password counter
        if it is > MAX_ATTEMPTS
            enter SECURITY LOCKOUT MODE for this account
        end
    end
end
if login is not OK
    generate a page with HTTP_REFRESH time of DELAY+1 seconds
    and a session ID, saying "User or password unknown,
    or you tried to login before HH:MM:SS (DELAY seconds).
    The page might also contain a Javascript timer, just in
    case. The delay is 1s more than necessary as a safety
    margin.
end
于 2013-04-21T17:14:10.670 回答
0

AES 256 位加密非常“强大”,这意味着它是难以破解的最新加密标准之一。我已经读到,如果您采用蛮力方法来破解它,您将需要世界上所有的计算能力。

查看:

如何在 PHP 中进行 AES256 解密?

和:

http://en.wikipedia.org/wiki/Advanced_Encryption_Standard http://www.andrew-kirkpatrick.com/2013/01/aes-encryption-with-php-256-bit-using-iv/

希望有帮助。

于 2013-04-21T17:14:04.117 回答