如何在 Symfony 4 中找出哪些值对 memory_cost、线程和 time_cost 适用于 Argon2i?

How to find out what values are good for memory_cost, threads and time_cost for Argon2i in Symfony 4?

我想将 Argon2i 用于我的 Symfony 4 编码器,因为我看过多篇关于它比 bcrypt 或 pbkdf2 更好的文章。不幸的是,我真的不知道我系统上的 memory_cost、time_cost 或线程使用什么值。什么是好的价值观,我怎样才能弄清楚我的价值观应该是什么?

什么是好的价值观?

根据您的服务器,这是非常主观的。 @Martin 对此 and you can read the RFC specs here 做了相当好的描述。但总而言之(也许可以根据我阅读的几十篇文章分享我个人的 opinion/best 答案)- "good" 值是:

  • 散列时间。 在正常系统上使散列在 0.5 毫秒到 500 毫秒之间(更高的值提供更高的安全性但用户体验更慢)或 1 秒以上在高安全性系统上。同样,即使是这些数字也是非常主观的,这似乎只是基于我所读内容的普遍共识。您希望散列花费这么长时间的原因是因为当黑客试图破解散列时,每次散列尝试都会花费那么多时间。正常的黑客尝试可能会在几毫秒内发生,因此将其拉长以使其花费更长的时间基本上会使破解散列花费永远的时间,因此黑客将(希望如此?)放弃。

  • 内存。 更大的服务器将拥有更大的 RAM。就我个人而言,此时我会说 128MB 在具有相当大 RAM 和良好 cpu 的服务器上可能是您想要的最大值(在撰写本文时),但这只是我的观点。这个值实际上很大程度上取决于您的 Web 服务器通常首先使用的内存 RAM——您不想在已经消耗大量 RAM 的服务器上使用大值。 Argon2 的默认值为 1MB。在默认情况下,找到以下时间

    • 通用云服务器 512 MB,1 核:3-5 毫秒
    • 通用云服务器 2 GB,2 核,1-3 毫秒
    • 512 MB Raspberry Pi 零:75-85 毫秒
  • Threads.基本上会占用CPU多少线程。与 memory_cost 一样,这种方式围绕着您服务器的 CPU 使用情况。根据我的经验,我将最大内核数除以一半,这通常是一个很好的数字(如果没有别的,首先),但你需要进行实验才能看到。

  • 时间成本。这个值应该最后算出来。从 1 开始,找出您想要的 memory_cost 和线程的值,然后按比例放大此值。根据我的经验,如果你得到 memory_cost 并且线程正确,这个数字不会高于 5,但这同样是非常有争议的。

综上所述(首先是这个 post 的目的),下面的测试试图帮助您找到适合 YOUR[=73 的值=] 系统。请注意,如果您更改了 hardware/OS/web 服务器 service/some 对服务器的重大更改,您应该始终重做这些计算。也不要 运行 在你的开发服务器上这样做,设置值并认为它们在你的生产服务器上是相同的 - 你应该在你打算使用代码的每台服务器上这样做。

测试:

下面是一个简单的测试,只涉及一个 twig 文件和一个控制器。然后它将使用具有指定值的 Argon2i 执行散列。将迭代次数设置为大于 1 将使我们能够计算平均值(可能更准确)。最后它会给出一个差值,并用简单的红色或绿色标记它,以便告诉你结果是什么。

树枝:

<form action="{{ path('test') }}" method="post">
    <p>
        <label for="time_cost">Time Cost:</label>
        <input type="text" id="time_cost" name="time_cost" value="{{ last_time_cost }}" />
    </p>
    <p>
        <label for="memory_cost">Memory Cost:</label>
        <input type="text" id="memory_cost" name="memory_cost" value="{{ last_memory_cost }}" />
    </p>
    <p>
        <label for="thread_cost">Thread Cost:</label>
        <input type="text" id="thread_cost" name="thread_cost" value="{{ last_thread_cost }}" />
    </p>
    <p>
        <label for="iterations">Iterations (how many times to compute the hash):</label>
        <input type="text" id="iterations" name="iterations" value="{{ last_iteration }}" />
    </p>
    <p>
        <label for="desired_time">Desired Time in seconds. Normal system: 0.5, High security: 1 (or higher).:</label>
        <input type="text" id="desired_time" name="desired_time" value="{{ last_desired_time }}" />
    </p>
    <p>
        <label for="password">Password to hash:</label>
        <input type="text" id="password" name="password" value="{{ last_password }}" />
    </p>
    <button type="submit">Run Test</button>

    <div>Average seconds taken (over {{ last_iteration }} iterations): {{ total }}</div>
    <div>Ideal seconds taken: {{ last_desired_time }}</div>
    <div>Difference: <span style="color:{{ style }}">{{ diff }}</span></div>
</form>

控制器:

/**
 ** @Route("/test", name="test")
 */
public function test(Request $request)
{
    $time_cost = $request->request->get('time_cost');
    $memory_cost = $request->request->get('memory_cost');
    $thread_cost = $request->request->get('thread_cost');
    $desiredTime = $request->request->get('desired_time');
    $iterations = $request->request->get('iterations');
    $password = $request->request->get('password');

    if (empty($memory_cost) || !is_numeric($memory_cost))
        $memory_cost = 16384;

    if (empty($time_cost) || !is_numeric($time_cost))
        $time_cost = 2;

    if (empty($thread_cost) || !is_numeric($thread_cost))
        $thread_cost = 4;

    echo $desiredTime;

    if (empty($desiredTime) || (!is_numeric($desiredTime) &&!is_float($desiredTime)))
        $desiredTime = 0.25;

    if (empty($iterations) || !is_numeric($iterations))
        $iterations = 10;

    if (empty($password))
        $password = 'correct horse battery staple';

    $options = [
        'memory_cost' => $memory_cost,
        'time_cost' => $time_cost,
        'threads' => $thread_cost
    ];

    $totalTime = 0;
    for($i = 1; $i <= $iterations; $i++)
    {
        $start = microtime(true);
        password_hash($password, PASSWORD_ARGON2I, $options);
        $end = microtime(true);
        $total = $end - $start;
        $totalTime = $totalTime + $total;
    }

    $diff = $desiredTime - $totalTime / $iterations;

    if ($diff > 0 || $diff < -0.50)
        $style = 'red';
    else
        $style = 'green';

    return $this->render('index/test.html.twig', array(
        'last_thread_cost' => $thread_cost,
        'last_time_cost' => $time_cost,
        'last_memory_cost' => $memory_cost,
        'last_iteration' => $iterations,
        'last_desired_time' => $desiredTime,
        'last_password' => $password,
        'total' => $totalTime / $iterations,
        'diff' => $diff,
        'style' => $style,
    ));
}

获得适用于您的环境的值后,您可以在 security.yaml

中为您的编码器设置它们

security.yaml /app/config/packages/security.yaml

注意:这有示例值。请勿使用它们(w/o 测试),使用上面的内容来查找 您的 值。

security:
    ...
    encoders:
        App\Security\SecurityUser:
            algorithm: argon2i
            memory_cost: 102400
            time_cost: 3
            threads: 4