Securely store user data in DB with Symfony - php

I want to store data from users so that they become useless even if the database gets leaked somehow. I also don't want to be able to encrypt the data, so I encrypt all my data via `openssl_encrypt' like this:
$passCode = base64_encode($this->get('session')->get('_pk'));
if (strlen($passCode) <= 16) {
$iv = str_pad($passCode, 16, '0');
} else {
$iv = substr($passCode, 0, 16);
}
$ciphertext = openssl_encrypt('whatevervalue', 'AES-256-CBC', $passCode, 0, $iv);
$test = new Test();
$test->setValue($ciphertext);
...
$em->persist($test);
$em->flush();
...
The $passCode is actually their password, which I put into the session var like this:
SecurityListener.php
<?php
namespace AppBundle\Listener;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class SecurityListener
{
public function __construct($security, Session $session)
{
$this->security = $security;
$this->session = $session;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$this->session->set('_pk', base64_encode($event->getRequest()->get('_password')));
}
}
2 Problems:
Storing the $passCode (whitout knowing actually that much about sessions) seems to be a security issue possibly?
What happens if a user changes the password. With the current way I'd need to decrypt and re-encrypt all his DB data with the new password, so that does not seem like a proper solution. What if he looses his password?
Maybe it is easier to understand what I want to here:
I want to encrypt all data in my database that the user himself enters there. I want it to be a "feature" that even the admin(s) cannot read the data without the key. I know the latter is not 100% possible (as there will be ways to intercept passwords/keys if entered through a web interface, but at least that invloves some change of code). Is something like that even possible at all? Any open source porjects I can have a look at?
Thank you!

Have some invariants for each user that will be used to generate the private key: user ID, account creation date, some random generated salt...
Write a hashing function that will generate the private key.
Obfuscate the code of the function.
This is not perfect, since people with access to the code and the database will be able to decipher the data, but it should be safe against database leaks and it doesn't have the problems you mention.
The only safer solution I can think of is to have users generate their own private keys and encrypt the data on client side, then send the encrypted data to the server. Otherwise, if the server has the tools necessary to decrypt, there will always be the way for someone with complete access to the system to decrypt the sensitive data.
I'm no expert in this matter and please tell me if I'm wrong.

Related

Password Hash Algorithm

I don't know much about hashing passwords, but I'd like to know. I'd like to know how good the following algorithm is for a normal site without credit card information or something like that, and I also want to know how to improve it.
The algorithm is:
hash('sha512', crypt(hash('whirlpool', $password.$username), base64_encode(md5(strlen($password)))))
Don't mix more than one hash, each one is optimized to work best by itself.
Depending on what you are using that hash for it's also a very bad idea to put $password in it. If it is being stored on the user's computer that is, like in a cookie. You don't want that in there.
If you store the hash in a database you can also make it better by adding a dynamic random string before using the hashing algorithm. Then a new hash will be generated for the user each visit.
I would highly recommend using a well-known, tested, vetted hash/crypt function over any home-grown algorithm.
Here is a class that I created to store a id/password combos for an api I integrate with. Each user can have their own unique credentials. I do not advise storing any credit card data on a non PCI compliant computer.
This is my exact class but you have some missing pieces so I have commented those. Please note that the vector is unique (Think of it as a hash) and I store that in the database along with the encrypted data.
The key is out of the public directory which goes to another topic of securing your box.
<?php
// This is on my index page but added here so you see all constants.
define('DIR', dirname(__FILE__) . '/');
class locker {
private $algorithm = MCRYPT_RIJNDAEL_256;
private $key;
private $mode = MCRYPT_MODE_CBC;
public $iv; // Public so we can change to the one used to encrypt it.
public function __construct()
{
// Lets include our key
// The key is located Outside of the public directory.
$this->key = file_get_contents(DIR .'../keys/passphrase.key');
// Create the initialization vector for added security.
$this->iv = mcrypt_create_iv(mcrypt_get_iv_size($this->algorithm, MCRYPT_MODE_ECB), MCRYPT_RAND);
}
public function encrypt($string)
{
return base64_encode(mcrypt_encrypt($this->algorithm, $this->key, base64_encode($string), $this->mode, $this->iv));
}
public function decrypt($string)
{
return base64_decode(mcrypt_decrypt($this->algorithm, $this->key, base64_decode($string), $this->mode, $this->iv));
}
// Helper functions so you can see what you can do on your own box.
public function list_modes()
{
print_r(mcrypt_list_modes());
}
public function list_algorithms()
{
print_r(mcrpt_list_algorithms());
}
}
?>
<?php
//Example usage
$locker = new locker;
$pass = $locker->encrypt('passwordvalue');
$iv = $locker->iv;
// Decrypt it
$locker = new locker;
$locker->iv = $iv;
$pass = $locker->decrypt($pass);
?>
If you want something strong. you have to
- never save the password but a hash (don't need so much) to avoid db hack use of password.
and never ask for the password but for a hash of the pasword hash + salt (the date for example) to avoid playback attack
try this (very easy to use) class I wrote which includes automatic algorithm detection to get the most secure algorithm that your server supports: http://netentwicklung.wordpress.com/2012/06/17/87/

How can I load a function just once and store the return in a variable?

I'm currently going over my user registration code. The part I'm focusing on right now is the password hashing part.
What I do is get a static_salt from a config file, and use mt_rand() to generate a dynamic_salt. What I want to do is have this dynamic_salt stored in my database.
But if I pass the dynamic_salt() method to the create method in order to send it to the salt column of a table in my database it will just run the method again and create a different result from the one produced in my hashed() method.
What would be the best way to achieve what I'm trying to achieve, could you show me an example if possible?
public function create() {
$dbcolumn->password = $this->hashed();
$dbcolumn->salt = $this->dynamic_salt;
$this->db->insert('users', $dbcolumn);
}
public function dynamic_salt() {
$get_dynamic_salt = mt_rand();
return $get_dynamic_salt;
}
public function hashed() { //hashing method, that also makes
// sha1 and salt password
$static_salt = $this->config->item('encryption_key'); //grab static salt from config file
$dynamic_salt = $this->dynamic_salt();
$password = $this->encrypt->sha1($this->input->post('password')); //encrypt user password
$hashed = sha1($dynamic_salt . $password . $static_salt);
return $hashed;
}
I recommend that you don't use a dynamic salt, as it will reduce your applications flexibility and is likely not to work in it's current form.
The purpose of a salt is to prevent against dictionary attacks that someone could do if they obtained your user database. In that regard, a static salt is definitely a good idea to implement in your application.
Adding a dynamic salt to each user would mean that you will have to hit a datastore to retrieve the dynamic salt and the hashed version of the user's password, then you will have to perform the CPU intensive hash function (in your code, twice -- you are hashing a hash which is less secure and more likely to have collisions).
Having a simple known, static salt, and a hashed password will allow you to use key/value storage systems like memcache should you application grow. Store the userid as the key and the users hashed password as the value and you will have a lightening fast authentication system.
Try this:
public function dynamic_salt() {
if(!isset($this->dyn_salt))
$this->dyn_salt = mt_rand();
return $this->dyn_salt;
}
If the dyn_salt instance variable doesn't already exist, it will assign it the result of mt_rand(), else it will just return the previous value.

Zend_Auth setCredentialTreatment

I'm using Zend_Auth with setCredentialTreatment to set the hash method and salt. I see all examples doing something like this, where the salt seems to be inserted as a text.
->setCredentialTreatment('SHA1(CONCAT(?,salt))'
but my salt is stored in the database. I could retrieve it first then use it in setCredentialTreatment but is there a way I could define it directly as a field name, so setCredentialTreatment would know to get it from that field? sort of like the way we define the field name for the username or password
->setCredentialColumn('password')
A side issue I'm having is that I'd like to use SHA512 not SHA1. Is this possible or is it not available? All the examples I see using SHA1.
I should say I'm fairly new to zend and am porting an existing application, so please go easy on me with the answers.
The example you've given does use the salt as stored in the database. It will work as long as the salt is stored in each row in a field called 'salt'. If the salt was not in the DB and in a PHP variable instead, the code would be something more like:
->setCredentialTreatment("SHA1(CONCAT(?, '$salt'))")
As for using SHA512, this might be a little trickier. Assuming you're using MySQL, SHA1() in this case is a MySQL function, and MySQL does not have a function for SHA512 as far as I can tell, and neither does PHP (edit: I was wrong about the latter, see comments). So you'll have to implement your own PHP SHA512 function, load the salt for the user out of the DB first, hash the result and not do anything to the variable in setCredentialTreatment.
As the other answer suggested you might want to write your own Zend_Auth_Adapter for this. An auth adapter is a class that handles authentication, presumably at the moment you're using Zend_Auth_Adapter_DbTable. You can find some more info about auth adapters in the manual: http://framework.zend.com/manual/en/zend.auth.introduction.html
Here's an example:
class My_Auth_Adapter extends Zend_Auth_Adapter_DbTable
{
public function authenticate()
{
// load salt for the given identity
$salt = $this->_zendDb->fetchOne("SELECT salt FROM {$this->_tableName} WHERE {$this->_identityColumn} = ?", $this->_identity);
if (!$salt) {
// return 'identity not found' error
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, $this->_identity);
}
// create the hash using the password and salt
$hash = ''; // SET THE PASSWORD HASH HERE USING $this->_credential and $salt
// replace credential with new hash
$this->_credential = $hash;
// Zend_Auth_Adapter_DbTable can do the rest now
return parent::authenticate();
}
}
You can write your own Zend_Auth_Adapter.

Role of Session in user authentication/login?

I was hoping someone could help me with a question I've come up on.
I have a Session object that handles storage of general session data, I also have a Authentication object which validates a users credentials.
Initially I passed the desired Authentication class name to my Session object then had a login method that created an instance of the Authentication object and validate the credentials. I stored the result of this validation in a Session variable and made it available via a getter. The user data was also stored in the Session for later use. In addition to all this, I have a logout method, which removes the user data from the Session and thus logging the user out.
My question is what role should the Session object play in users logging into their account?
And what other ways might one suggest I go about handling user login, as it stands right now I feel as though I'm getting too much wrapped up in my Session object.
Simply calling your authenticate method should trigger logic within Auth to store the proper data in the session (or some other data store) and Auth should also be used exclusively to retreive/revoke this info. So using the example form your comment it might be:
class Auth {
public static function authenticate($identity, $pass)
{
// do lookup to match identity/pass if its good then
/* assume $auth is an array with the username/email or
whatever data you need to store as part of authentication */
Session::set('auth', $auth);
return true;
// if auth failed then
Session::set('auth', array('user'=>'anonymous'));
return false;
}
public function isAuthenticated()
{
$auth = Session::get('auth');
if(!$auth)
{
return false;
}
return (isset($auth['user']) && $auth['user'] !== 'anonymous');
}
}
[...] as it stands right now I feel as
though I'm getting too much wrapped up
in my Session object.
And id agree. Idelaly for authentication/credentials you shoudl only be interacting with the Auth/Acl object(s). They would then utilize the session as stateful store... but you shouldnt care that its even stored in session. The code utilizng the Auth/Acl object(s) should be completely unaware of this fact.
For example:
//Bad
if($session->get('authenticated', 'auth'))
{
// do stuff
}
// also bad
if(isset($_SESSION['authenticated']))
{
// do stuff
}
//Good
if($auth->isAuthenticated())
{
// do stuff
}
// inside $auth class it might look like this
public function isAuthenticated()
{
$store = $this->getSotrage(); // assume this returns the $_SESSION['auth']
return isset($store['authenticated']);
}
The session is a good place for holding user data that you want to have managed in some sort of state across various pages or if you need a fast accessible way to get at it without hitting the database. It is a bad idea to keep secure information (re: passwords/etc) in the session, but rapid access information like username, name, email address, preferences, etc is all good data to put in the session. Try to keep it simple though.
Keep in mind though, that the session (or related cookie) should only ever be used for identification. It should not be used for authentication.
Authentication object is a good method. Make sure that it only holds secure information as long as it needs to and that it has all the necessary functions available to keep sensitive data protected.

sodium_crypto_auth_verify vs hash_equals for Token Authentication

I am building my own small framework for my final year project at university and I am very confused about best practices here. I have read quiet a lot of articles and have a general idea but some clarification would be better.
I really like the new sodium extension in PHP But I am a bit confused.
I am creating a split token authentication thing both for long term persistent cookies and password resets.
I am using Libsodium as much as possible as it seems very secure, all be it new and not very well documented.
I am creating a split token like selector:validator.
What I want to do is basically use the selector to query the DB,
Then I want to either compare the two hashes VS Hash the plain text version from the cookie and then compare (But the latter means keeping a key somewhere which creates an issue)
I have heard across many articles especially with Paragone suggest that it is preferable to store the hashed version of a token in the Database and the plain version in the cookie or token.
Is there any real benefit of this?
I have created a simple Token class:
class SplitToken extends Token
{
protected $selector;
protected $validator;
function __construct($selector=14, $validator=18)
{
$this->selector = bin2hex(random_bytes($selector));
$this->validator = bin2hex(random_bytes($validator));
$this->key = random_bytes(SODIUM_CRYPTO_AUTH_KEYBYTES);
$this->tokenHash = sodium_crypto_auth($this->validator, $this->key);
}
public function Set()
{
$this->token = $this->selector.':'.$this->validator;
return $this;
}
public function Get()
{
return $this->token;
}
$sptoken = new SplitToken();
$token = $sptoken->set()->Get();
$dt = new DateTime('+ 2 months');
$expiry = $dt->getTimestamp();
//Gets bin2hex version of validator side of token for DB
$validatorHash = $token->GetValidatorHashHex();
$key = $token->GetKey();
//Store token In DB:
$query = "UPDATE Users SET Selector, Validator, Expiry
WHERE Selector = :Selector and Validator = :Validator and Expiry = :Expiry;
$stmt = $pdo->prepare($query);
$stmt->execute(
['Selector' => $token->GetSelector(),
'Validator' => $validatorHash,
'Expiry' => $expiry]);
Now that the temp token is in the DB. Now it is time to set the cookie (The same can apply to PW Reset with a different expiry of course.
Here I am getting confused and I have two options:
Option 1:
//Store the hexed version of selector:validator in the cookie (but not hashed)
setcookie('auth_token', $token, $expiry, '/', 'CONST_DOMAIN', true, true);
//Where do I store the key?
//So far I am using JSON fuNCTION which gets key from the folder where it is stored:
$storedKey = Key::GetFromVault('auth_token');
if(isset($_COOKIE['auth_token')){
$cookie = explode(':', $_COOKIE['auth_token'];
//Gets the User from the DB Where Selector = Selector
$user = DB::SelectUser($user);
//If User exists
if($user){
//*** Checks the Hash separate from the query to avoid timing attack ***
var_dump(sodium_crypto_auth_verify($user->Validator, $cookie[1], $storedKey);
}
}
Option 2:
Seems a bit simpler and cleaner because I am simply comparing two hashes and there is no need to worry about the key later but it means that I have to store the hashed version of the validator in the cookie:
$hashedToken = $token->GetSelector.':'.$token->GetValidatorHashHex();
setcookie('auth_token', $hashedToken, $expiry, '/', 'CONST_DOMAIN', true, true);
//Now On Request:
if(isset($_COOKIE['auth_token')){
$cookie = explode(':', $_COOKIE['auth_token'];
$user = DB::SelectUser($user);
if($user){
//Checks the Hash separate from the query to avoid timing attack
var_dump(hash_equals($user->Validator, $cookie[1]);
}
}
I know this probably sounds silly and it does not make much difference,
But In option 1 I am comparing a plain text token to a hashed version and comparing it with the sodium function
And with Option 2 I am comparing two hashes
If I compare the same two hashes with the sodium function it returns false and if I compare the plain text to the hashed with hash_equals even though the token before the hash is the same it returns false.
So basically:
1) Does this make much of a difference?
2) I would like to find a neat solution for Key storage and then store plain in the cookie but not sure how
Any advice would be greatly appreciated.
Happy to clarify my question
Thanks
It doesn't make any difference.
What sodium_crypto_auth_verify() does is compute the hash, and compare it with the provided one.
As long as the comparison is in constant time, there is no difference between this and doing it yourself.

Categories