简体   繁体   中英

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

I want to use Argon2i for my Symfony 4 encoder as I've seen multiple articles about how it's supposedly better than bcrypt or pbkdf2. Unfortunately I don't really know what values to use for the memory_cost, time_cost or threads on my system. What are good values and how can I figure out what my values should be?

What are good values?

This is HIGHLY subjective based on your server. @Martin has done a reasonbly good description of this here and you can read the RFC specs here . But to summarize (and perhaps share my personal opinion/best answer just based off the dozens of articles I've read) - "good" values are ones that:

  • Hash time. Make the hash take somewhere between 0.5ms - 500ms on a normal system (higher values offer more security but slower user experiance) or 1s+ on a high security system. Again even THOSE figures are highly subjective, that just seems to be a general consensus based on what I've read. The reason why you want the hash to take this long is because when a hacker attempts to crack the hash it will take that amount of time per hash attempt. Normal hacking attempts can happen in a few milliseconds so stretching it out so it takes longer will basically make it take forever to crack the hash and therefore the hacker will (hopefully?) give up.

  • Memory. Bigger servers will have bigger amounts of RAM. Personally at this time I'd say 128MB on a server with a fairly large amount of RAM and a good cpu would probably be the max you would want to go (at the time of this writing), but that's just my opinion. This value really depends a lot on how memory RAM your web server typically uses in the first place - you don't want to use big values on servers that already consume a to of RAM. The default for Argon2 is 1MB. At that default the following times are found

    • Common Cloud Server 512 MB, 1 Core: 3-5 ms
    • Common Cloud Server 2 GB, 2 Core, 1-3 ms
    • 512 MB Raspberry Pi Zero: 75-85ms
  • Threads. Basically how many threads of CPU it'll take up. Like memory_cost, this kind of revolves around the CPU usage of your server. In my experiance I take the max number of cores and divide it by half and that typically is a good number (if nothing else, to start with), but you need to experiment to see.

  • Time Cost. This value should be figured out last. Start with 1, figure out the values you want for memory_cost and threads and then scale this value up. In my experiance if you get memory_cost and threads right this number doesn't get higher than 5, but again this is HIGHLY disputable.

That all being said (and the purpose of this post in the first place), the test below is an attempt to help you find values that will work for YOUR system. Note that if you ever change out your hardware/OS/web server service/some major change to your server you should ALWAYS redo these calculations. Also don't run this on your dev server, set the values and think they will be the same on your production server - you should do this on each server you intend to use the code on.

Test:

Below is a simple test which just involves a twig file and a controller. It will then execute the hash using Argon2i with the specified values. Setting the iterations to more than 1 will allow us to calculate an average (perhaps more accurate). In the end it will give a difference value and marks it with an easy red or green color in order to tell you what the result is.

Twig:

<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>

Controller:

/**
 ** @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,
    ));
}

Once you get values that work for your environment, you can set them for your encoder in security.yaml

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

Note: This has example values. DO NOT use them (w/o testing), use above to find YOUR values.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM