Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cooking with Credentials

Eric Mann
January 14, 2021

Cooking with Credentials

There are many different ways to authenticate users to your application in PHP land. This talk will take a deep look into at least three different patterns for user authentication to keep your system secure. Everything we’ll be doing is password-focused, moving from the simplest methods to the most complex. We will only discuss actually-secure PHP authentication, but presenting multiple approaches will give you and your team superb flexibility when you move from theory to implementation.

Eric Mann

January 14, 2021
Tweet

More Decks by Eric Mann

Other Decks in Technology

Transcript

  1. 1

    View Slide

  2. 2
    Hello.
    Eric Mann
    Director of Engineering, Vacasa

    View Slide

  3. 3

    View Slide

  4. 4
    What is a password?
    Basic authentication flows
    Cooking with salt
    Cooking with pepper
    Socially distant passwords
    Further resources and study

    View Slide

  5. 5
    Something you know that is information kept secret from others
    but which can be used to authenticate your identity to a system
    requiring identity verification and security.

    View Slide

  6. 6
    1
    First, you identify yourself to the system. This could be by username, email address,
    account ID, or some other means agreed-upon with the system.
    2
    Next, you have to prove you are who you say you are by confirming some pre-shared,
    secret detail with the system.
    3
    If everything lines up, you’re authenticated and logged in!

    View Slide

  7. 7

    View Slide

  8. 8
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.

    View Slide

  9. View Slide

  10. 10

    View Slide

  11. 11
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    To prevent rainbow table lookup attacks, it’s best to add a unique, random salt to each password
    during hashing. This dramatically slows brute force attacks and further protects your users.

    View Slide

  12. 12
    $hash = password_hash(/** ... **/, PASSWORD_DEFAULT);
    /** ... **/
    if (password_verify(/** ... **/, $hash) {
    // ...
    }

    View Slide

  13. 13

    View Slide

  14. 14
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Even with salting, you still send the plaintext of a password over the wire to the server. If the
    server is misconfigured or too chatty in the logs, this plaintext could be inadvertently leaked.
    const pepper = new TextEncoder().encode('...');
    async function getKeyMaterial(password) {
    let encoder = new TextEncoder();
    return window.crypto.subtle.importKey(
    'raw',
    encoder.encode(password),
    'PBKDF2',
    false,
    ['deriveBits']
    );
    }

    View Slide

  15. 15
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    async function pepperPassword(password) {
    let keyMaterial = await getKeyMaterial(password);
    let keyBuffer = await window.crypto.subtle.deriveBits(
    {
    'name': 'PBKDF2',
    'salt': pepper,
    'iterations': 100000,
    'hash': 'SHA-256'
    },
    keyMaterial,
    256
    );
    let keyArray = Array.from(new Uint8Array(keyBuffer));
    return keyArray.map(b => b.toString(16).padStart(2, '0')).join('');
    }

    View Slide

  16. 16
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    peppered = await pepperPassword('...');

    View Slide

  17. 17

    View Slide

  18. 18
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.
    Lorem ipsum dolor sit amet,
    consectetur adipiscing elit.
    Fusce elit ex, consequat et
    tincidunt non, pharetra non.

    View Slide

  19. 19
    $salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES);
    $seed = sodium_crypto_pwhash(/* ... */);
    $keypair = sodium_crypto_kx_seed_keypair($seed);
    $public_key = sodium_crypto_kx_publickey($keypair);
    // POST this data to the server to register
    $registration = [
    'identifier' => $email,
    'salt' => sodium_bin2hex($salt),
    'public_key' => sodium_bin2hex($public_key)
    ];

    View Slide

  20. 20
    $user = get_user_from_database($email);
    $keypair = sodium_crypto_kx_keypair();
    $public_key = sodium_crypto_kx_publickey($keypair);
    $keys = sodium_crypto_kx_server_session_keys($keypair, $user->key);
    $client_secret = $keys[1];
    $server_secret = $keys[0];
    $message = random_bytes(32);
    $hash = sodium_crypto_generichash($message, $server_secret);
    $proof = sodium_bin2hex($message . $hash);

    View Slide

  21. 21
    $keys = sodium_crypto_kx_server_session_keys($keypair, $server_key);
    $client_secret = $keys[0];
    $server_secret = $keys[1];
    $server_proof = sodium_hex2bin($server_proof);
    $message = substr($server_proof, 0, 32);
    $hash = substr($server_proof, 32);
    if (!hash_equals(
    sodium_crypto_generichash($message, $server_secret), $hash
    )) {
    exit;
    }

    View Slide

  22. 22
    $keys = sodium_crypto_kx_server_session_keys($keypair, $server_key);
    $client_secret = $keys[0];
    $server_secret = $keys[1];
    /* ... */
    $message = random_bytes(32);
    $hash = sodium_crypto_generichash($message, $client_secret);
    $proof = sodium_bin2hex($message . $hash);

    View Slide

  23. 23
    $client_proof = sodium_hex2bin($proof);
    $message = substr($client_proof, 0, 32);
    $hash = substr($client_proof, 32);
    if (!hash_equals(
    sodium_crypto_generichash($message, $client_secret), $hash
    )) {
    exit;
    }
    // Store the user's email in a session for subsequent requests
    $_SESSION['identifier'] = $email;

    View Slide

  24. 24

    View Slide

  25. 25

    View Slide

  26. 26

    View Slide

  27. 27

    View Slide

  28. 28

    View Slide