Link to home
Start Free TrialLog in
Avatar of Richard Korts
Richard KortsFlag for United States of America

asked on

AES Encrypt / Decrypt

Is thee a way to do AES Encryption & Decryption from php?

Specifically, I want passwords stored in a MySQL database to be encrypted. Of course they will have to be checked when a user logs in so I'm guessing I would have to decrypt to do a compare?

Thanks
Avatar of Sean Stuber
Sean Stuber

ideally you won't encrypt and decrypt a password

instead, use a cryptographically secure hashing algorithm (like bcrypt)
store the hash
when a user logs in, hash their password and compare that to the stored hash.

the reason you do this is, encryption is reversible (hence decryption) but hashing is not.


here's a nice example along with expanded explanations of what I have above.

https://alias.io/2010/01/store-passwords-safely-with-php-and-mysql/
Please see An Afterword About Storing Passwords
https://www.experts-exchange.com/articles/2391/PHP-Client-Registration-Login-Logout-and-Easy-Access-Control.html

Make a Google search for "Correct Horse Battery Staple"

Then if you still want to encrypt and decrypt information, you can use something like this:
<?php // demo/encrypt_decrypt_mcrypt.php
/**
 * Notice: MCrypt is sometimes considered "dead" in 2015
 * but the PHP documents are incomplete for OpenSSL
 *
 * Show how to encrypt and decrypt information
 * with binary-safe transport over the internet
 * Note: ECB may not be the "best" mode
 *
 * http://php.net/manual/en/book.mcrypt.php
 * http://php.net/manual/en/ref.mcrypt.php
 * http://php.net/manual/en/mcrypt.ciphers.php
 *
 * https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
 * https://en.wikipedia.org/wiki/Base64
 *
 * Parallel construction in the encrypt() decrypt() methods
 */
error_reporting(E_ALL);


/**
 * The Interface defines the two main data transformation activities
 */
Interface Encryption_Interface
{
    public function encrypt($text);
    public function decrypt($text);
}

class Mcrypt_Encryption Implements Encryption_Interface
{
    protected $key;

    public function __construct($key='quay')
    {
        // THE KEY MUST BE KNOWN TO BOTH PARTS OF THE ALGORITHM
        $this->key = $key;
    }

    public function encrypt($text)
    {
        // DECLOP WHITESPACE BEFORE ENCRYPTION
        $text = trim($text);

        // ENCRYPT THE DATA
        $data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->key, $text, MCRYPT_MODE_ECB);

        // MAKE IT base64() STRING SAFE FOR STORAGE AND TRANSMISSION
        return base64_encode($data);
    }

    public function decrypt($text)
    {
        // DECODE THE DATA INTO THE BINARY ENCRYPTED STRING
        $text = base64_decode($text);

        // DECRYPT THE STRING
        $data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->key, $text, MCRYPT_MODE_ECB);

        // DECLOP NUL-BYTES BEFORE THE RETURN
        return trim($data);
    }
}


// INSTANTIATE AN ENCRYPTION OBJECT FROM THE CLASS
$c = new Mcrypt_Encryption();

// INITIALIZE VARS FOR LATER USE IN THE HTML FORM
$encoded = $decoded = NULL;

// IF ANYTHING WAS POSTED SHOW THE DATA
if (!empty($_POST["clearstring"]))
{
    $encoded = $c->encrypt($_POST["clearstring"]);
    echo "<br/>{$_POST["clearstring"]} YIELDS ENCODED ";
    var_dump($encoded);
}

if (!empty($_POST["cryptstring"]))
{
    $decoded = $c->decrypt($_POST["cryptstring"]);
    echo "<br/>{$_POST["cryptstring"]} YIELDS DECODED ";
    var_dump($decoded);
}

// CREATE THE FORM USING HEREDOC NOTATION
$form = <<<FORM
<form method="post">
<input name="clearstring" value="$decoded" />
<input type="submit" value="ENCRYPT" />
<br/>
<input name="cryptstring" value="$encoded" />
<input type="submit" value="DECRYPT" />
</form>
FORM;

echo $form;

Open in new window

There are some articles here for this very thing that will outline what you want to do
 https://www.experts-exchange.com/articles/2391/PHP-Client-Registration-Login-Logout-and-Easy-Access-Control.html

https://www.experts-exchange.com/articles/9739/PHP-MySQL-User-Authentication.html

In simple pseudo code  
newPassword = "FidoMyDogsName"
salt1 = username
salt2 = some_random_string
salt3 = something_else

hashPass = hash(concatenate(newPassword, salt1,salt2,salt3)) //the hash will be a long text string

// store the hashed password to the database

// to test the pass
posted_password = "FidoMyDogsName"
//test the password against what is in the db
salt1 = username
salt2 = some_random_string
salt3 = something_else
password_test = hash(concatenate(posted_password , salt1,salt2,salt3))
if(passwword_test === pass_stored_in_database){
   // password is good
} else {
   // password is bad
}

Open in new window

Avatar of Richard Korts

ASKER

Scott Fell,

I converted your pseudo code to real php; it works.

But the issue I see is where do I store salt1, salt2 & salt3? In the database? I can see that salt2 can be a different random value for each client.

But if this stuff is in the database and the objective is to avoid database hacking, aren't the "keys" to the kingdom already there in the database?
a salt isn't a key but is necessary to duplicate the hash.  you would need to store them in the database or someplace where the authenticating code can access them so it can apply the salt to the candidate password and generate the hash for comparison to the stored hash.
Exactly, you don't have to use salt, but it helps to create a more secure hash.  In my sample I used the username.  You know the username either from the user entering data or a database result.

You can stop there, but you can add an additional hash key value that is just hardcoded.  Let's say you have hard coded salt2  = "zhsLysk82ka;zkdjhf@svx!"  Just make sure to use the same hard coded information throughout your code.  It is probably best to not store salt keys in your db anyway.  The reason why you are creating the hash is if the db is compromised. If your entire server is compromised, that is is a different story.

There are more advanced ways of dealing with this where the hash is random or changes every log in.  At a minimum I would use something like the username and random string that you hard code.
Scott,

So you suggest just hard code the salt2 in the few (I think only two) programs where it is needed.  And as you say, if the entire server is compromised, that is a much bigger issue.

Richard
That is one way of doing it.

Have a read of the first 2 articles below and they will really help you understand.  I used both of them myself a few years ago. Just know they are old and when they talk about md5 for instance, that is easy to crack with today's computing power. If you can use a random hash, it will be more secure.  The idea of what I outlined is right.

https://crackstation.net/hashing-security.htm  (points to http://php.net/manual/en/function.mcrypt-create-iv.php)

http://arstechnica.com/security/2013/05/how-crackers-make-minced-meat-out-of-your-passwords/

http://arstechnica.com/security/2015/08/cracking-all-hacked-ashley-madison-passwords-could-take-a-lifetime/
greetings Richard Korts, , there are many factors to any security concerns in password database storage, alot of the info on the web about it, is many times outdated, and-or using ideas that the author of the article does not really know, but just read from some other article, that the author of that other one, took info from still another previous article. The way an experienced coder will try an use a way to get passwords, is to "Sign UP" on that site with at least 4 different "User Names" , email addys, and different passwords. Then use an SQL injection procedure to list out all user names, emals and passwords in the user table. Then the 4 user names that they used to "Sign Up" for that site are found, with each DB column entry for the password, if the passwords are all 16 characters in length (after removing the HEX encode), then they pretty much Know, it's a standard PHP MD5 hash, and they just use a 92 keyboard character hash "Look Up Table" for MD5 to find the actual text in the hash, if any of what are incorrectly called "Seeds" here, have been added to beginning, end or middle of a password, it will be a consistent length, and easily used to see and remove in the 4 different known passwords. A real seed needs to be used to actually change the hash method to use a different hash algorithm method, so it produces a non-standard result that can not be looked up in a standard hash "Look Up Table", to do this I always use the PHP  hash_hmac( )  function for any password hashing like -
$ps = hash_hmac('ripemd160', $ps, '*jE!cP)nt~,:lrFd$zO'.chr(197));

the hash_hmac( ) will actually CHANGE the hash output with the seed to a different XOR complied result , not in a standard hash result, and there are not many "Look Up Tables" made for 'ripemd160' anyway, so this is a hundred times more effective hashing for security concerns.
I hope this gives you more info about this.
To Slick812,

It appears to me that you have the best solution.

I'm a little concerned about some of your comments.

What we need is, when a user registers for our online service, they provide a password. We want to hash it & store the hash value in the database.

Then, the next time they log in, we hash the password they provided & do a lookup in the database comparing the previously hashed value (in the db) with the hash of the currently entered password; if equal, they are in, if not out.

So if I understand this, I would hash the originally entered password ($pwd), like this:

$ps = hash_hmac('ripemd160', $pwd, $key);

$key being some random string I store elsewhere in the database. Then I store $ps in the database, I'm guessing using like varchar(128) (in MySQL syntax).

Then on a subsequent login, I use the SAME  hash on the entered value & compared the hashed result in the database; if equal, they are good.

Is this correct?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Oh I forgot about the DB varchar(128) , if you use this -
    hash_hmac('ripemd160', $pwd, $key);
the output will be a HEX string  40 characters long, so for the BD Type you can use  varchar(40)

 there are many hashes available like with hash length (double for HEX) -
sha256    -32 length
sha512    -64 length
ripemd256    -32 length
tiger192,4     -24 length
whirlpool     -64 length
haval224,5     -28 length


the longer the hash, the more difficult it is to try and do a Look Up table to get the plaintext from a hashed string.

You may want to just use this  -
     hash_hmac('ripemd256', $pwd, 'J*&^gbF'.chr(214).'DeW{lO'.chr(177).'(bCzAs:+bFyDv~cK|');
with a 64 length HEX output, for a general security hash.
Thanks for the wealth of info.

I just hashed all the passwords in our user table & put the hashing in to the new registration (when pwd is first stored) & the the login check program.

One minor (but I thought not good thing) , when I first went to log in myself to test the login, I saw that the hashed password was in the form field for pwd, not my normal value.

I told FireFox (a LONG time ago) to save my password; of course obviously if I had left the hash value there it would have failed by hashing the hash.

Do you know of a way to avoid this? Is this only a FireFox issue (even if so, not good & very confusing to the average person).

Thanks