I know a salt isn't secure data that needs to be encrypted in the dB, but as its access should be controlled, is it considered safe to save it as a session variable at login for use later on different pages? Or should it be looked up in the dB with every request. My reasoning is less calls to the dB.
This is a home project password manager website, with a user base of one, but written as if it was for multiple users.
EDIT:
The login is handled via php utilising password_hash($password, PASSWORD_DEFAULT)
, which I believe handles its own salting, if I am understanding the manual correctly. The salting in question is for the users password database/manager, which uses a separate password for key verification generation to encrypt/decrypt the passwords. I thought I was using a single master salt on a per user basis, but it turns out that was incorrect.
Original Code:
function sodiumKeygen($password){
// create a 32 character secret key from the supplied password
$key = hash('tiger128,4', $password,false);
return($key);
}
function encryptPassword($pass,$key){
/* encrypt $pass using the sodium crypto library
return the encrypted value as base64 encoded $cipher
to store nicesly in DB*/
// nonce to append to cipher
$nonce = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$cipher = $nonce . sodium_crypto_secretbox($pass, $nonce, $key);
// destroy $pass and $key once no longer needed
sodium_memzero($pass);
sodium_memzero($key);
return($cipher);
}
And after reading the comments to the question made the changes:
function generateSalt($length=SODIUM_CRYPTO_PWHASH_SALTBYTES){
$salt = base64_encode(random_bytes($length));
return($salt);
}
function sodiumKeygen2($password, $saltBase64){
// create a 32 bit secret key from the supplied password and user salt
$length = SODIUM_CRYPTO_SECRETBOX_KEYBYTES;
// ops and mem limit work factor. 100 pw takes 5min at sensitive
// mode. set ops and mem to same level.
$opslimitLo = SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE;
$opslimitMid = SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE;
$opslimitHi = SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE;
$memlimitLo = SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE;
$memlimitMid = SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE;
$memlimitHi = SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE;
$algo = SODIUM_CRYPTO_PWHASH_ALG_DEFAULT;
$id = SODIUM_BASE64_VARIANT_URLSAFE;
$salt = base64_decode($saltBase64);
$key = sodium_crypto_pwhash($length, $password, $salt, $opslimitMid, $memlimitMid, $algo);
return($key);
}
function sodiumEncryptPassword($pass,$key){
/* encrypt $pass using the sodium crypto library
return the encrypted value as base64 encoded $cipher
to store nicesly in DB*/
$id = SODIUM_BASE64_VARIANT_URLSAFE;
// nonce to append to cipher
$nonce = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
// cipher is converted to base64 for DB storage
$cipher = sodium_bin2base64($nonce . sodium_crypto_secretbox($pass, $nonce, $key), $id);
// destroy $pass and $key once no longer needed
sodium_memzero($pass);
sodium_memzero($key);
return($cipher);
}
The salt generated by the function generateSalt()
is now unique to each password saved and stored along side it. It now makes sense to retrieve it alongside the password in the same query. There is no requirement to store it in a session variable so this question is now moot for my application.