Closed phavekes closed 2 days ago
Regarding the open questions:
stepup.sso_2fa
?@1 The only reservation that I have is that the user might not expect a persistent cookie, because the IdP\'s SSO cookies are typically session cookies. (Pieter van der Meulen - Oct 26, 2022)
@2 Agreed to make the cookie name configurable through parameters.yaml (Pieter van der Meulen - Oct 26, 2022)
@3 Halite looks like a solid library, nice find! Does it adds enough in addition to using Sodium directly to warrant adding another dependency? (Pieter van der Meulen - Oct 26, 2022)
@1 we decided upon creating a parameter config defining the cookie type: persistent/session. (Michiel Kodde - Oct 26, 2022)
@4 An SSO cookie is basically a short lived token. I want accountability for when we provide these, and when we use these.
(Pieter van der Meulen - Oct 26, 2022)
See https://www.pivotaltracker.com/story/show/183511341 and https://www.pivotaltracker.com/story/show/183511348 for logging (Peter Havekes - Oct 26, 2022)
@4 Instead of adding a uuid for a cookie. It might be even more interesting to add a sha256 hash of the contents of the hash. This enables us to see if the contents of the cookie changed. Between us issuing it, and the user showing it again in an SSO context. (Michiel Kodde - Oct 26, 2022)
I updated the description to summarize the discussion in the thread above. (Michiel Kodde - Nov 2, 2022)
Questions regarding the crypto solution we are about to implement.
First let me describe the workflow I\'m proposing.
Asterisks mark questions I have about the process.
Basically the oposite flow from the encryption
@phavekes @pmeulen (Michiel Kodde - Nov 10, 2022)
@3. The MAC can be stored in the cookie
@4. Yes, the key can be in the paramaters.yaml
(Peter Havekes - Nov 10, 2022)
@1. You can do encryption and authentication separately, this is probably what you are most familiar with. You then need to choose what to do first (encryption or authentication). Typically you\'d want to encrypt first and then sign, this is because many encryption algorithms are vulnerable to oracle attacks when decrypting unverified data, so what you propose is the wrong way around. However there are now also algorithms that do the authentication and encryption in one go. E.g. GCM, that is the type of algorithm that I propose we use.
@2. I want to use a separate authentication and encryption key that is only used for this purpose. I don\'t see a reason to derive the key using e.g. IdentityId. Salting is the job of the algorithm we choose. @3. A MAC is not sensitive so can be stored in the cookie, like @phavekes said. For this design we do not want to store anything related to the SSO cookie in a session store. @4: Yes, and as said in point 1 above, do not use it for anything else. (Pieter van der Meulen - Nov 10, 2022)
Thanks for the feedback kind sir\'s!
As requested during standup: here\'s some pseudo code denoting the current situation.
Where Crypto is the Paragonie Halite library class)
$cookieValue = \'JSON string containing the idenity, SF, timestamp,..\';
$encryptionKey = \'the encryption key configured in parameters.yaml\'
$authKey = \'the auth key configured in parameters.yaml\';
$mac = Crypto::authenticate($cookieValue, $authKey);
$encryptedData = Crypto::encrypt($cookieValue, $encryptionKey);
// Todo return a value object containing the MAC + Encrypted data
return $encryptedData;
$decryptedData = Crypto::decrypt($cookieData, $encryptionKey);
$verified = Crypto::verify($decryptedData, $authKey, $mac);
if (!$verified) {
// Detonate a bomb
}
return CookieValue::fromJson($decryptedData);
As already stated in the feedback.
@michielkodde You\'re using Halite above. It\'s encryption is already authenticated, so a separate authenticate/verify is unnecessary. (Pieter van der Meulen - Nov 10, 2022)
Ah, so the auth feature is there just for when you want to use that feature, and not encrypt your payload. (Michiel Kodde - Nov 10, 2022)
Example encrypting and decrypting using paragonie/halite 4.8, initialising the secret key from a hex key.
<?php
require_once \'vendor/autoload.php\';
// We want to encrypt and authenticate (i.e. protect against tampering) the cookie value using a long lived key.
// We can only use pseudorandom nonces, so nonce uniqueness depends on PRNG strength and nonce size.
// For the purpose of protecting the SSO, protecting the cookie against tampering is critical
// hiding it\'s contents is for defense in depth
// For Halite 5.0 the min supported version is PHP 8.0
// Halite 4.8 is the latest version that works with PHP 7.2. The discussion below is for 4.8.
// Halite uses libsodium
use ParagonIE\Halite\Halite;
use ParagonIE\HiddenString\HiddenString;
// The secret key is used for the authenticated encryption (AE) of the SSO cookies, it is stored in the parameters.yaml of the
// Stepup-Gateway. This secret may only be used for the AE of the SSO cookies.
// The secret key must be 256 bits (32 bytes)
// We use hex encoding to store the key in the configuration, so the key will be 64 hex digits long
$hex_encoded_secret_key_from_parameters = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
// Convert the key from the configuration from hex to binary. sodium_hex2bin
$secret_key_from_parameters = sodium_hex2bin($hex_encoded_secret_key_from_parameters);
assert( strlen($secret_key_from_parameters) == 32 );
$secret_key = new \ParagonIE\Halite\Symmetric\EncryptionKey(new HiddenString($secret_key_from_parameters) );
$plaintext_in = new HiddenString("The SSO cookie value to encrypt and MAC");
// Halite always uses authenticated encryption. See: https://github.com/paragonie/halite/blob/v4.x/doc/Classes/Symmetric/Crypto.md#encrypt
// It uses XSalsa20 for encryption and BLAKE2b for message Authentication (MAC)
// The keys used for encryption and message authentication are derived from the secret key using a HKDF using a salt
// This means that learning either derived key cannot lead to learning the other derived key,
// or the secret key input in the the HKDF. Encrypting many messages using the same secret key is not a problem in this
// design. This makes it a much safer setup than using GCM with the secret key directly:
// - GCM has a relatively short nonce (96 bits)
// - An attacker that is in possession of two different GCM messages that were encrypted using the same key
// can not only decrypt the two messages, but can also recover (parts) of the encryption key.
// Encryption:
$encrypted_and_authenticated_data = \ParagonIE\Halite\Symmetric\Crypto::encrypt(
$plaintext_in,
$secret_key,
Halite::ENCODE_BASE64URLSAFE // The default
);
echo \'Ciphertext = \' . $encrypted_and_authenticated_data . "\n";
// Decryption
$plaintext_out = \ParagonIE\Halite\Symmetric\Crypto::decrypt(
$encrypted_and_authenticated_data,
$secret_key,
Halite::ENCODE_BASE64URLSAFE // The default
);
echo \'Plaintext = \' . $plaintext_out->getString(). "\n";
// TODO: Handle exceptions
``` (Pieter van der Meulen - Nov 10, 2022)
Thanks, my second implementation matched your code example almost perfectly. I only added the hex2bin encoding/decoding step to the setting/parsing of the encryption key. And in addition to that, I reused most of your comments. See: https://github.com/OpenConext/Stepup-Gateway/commit/15b445e9d091d62016722a390852b5ae512d970d for the results. (Michiel Kodde - Nov 15, 2022)
This issue is imported from pivotal - Originaly created at Sep 28, 2022 by Peter Havekes
The goal is to store all in information that is required to determine if the SSO session is valid in this cookie. We do not want to use the PHP session cookie for this purpose, but a separate cookie for this purpose only.
Store this data in an encrypted and authenticated cookie in the browser. The cookie should be valid for the SSO-session lifetime this is configured in the gateway. Set this cookie only if the user successfully authenticated the actual token. Do not set the cookie during a SSO authentication. See #183511012 for when NOT to set this cookie. Each eligible token authentication sets the SSO cookie, thereby overwriting a previous value of the SSO cookie.
The cookie must be scoped to the hostname of the gateway so that only the gateway receives this cookie from the user\'s browser.
OpenQ&A (see comments below for discussion):enum: persistent|session
string
Note that the cookie name (sh|c)ould follow RFC 6265 (page 17) name convention.Est: 10..12h