security - Two takes on PHP two way encryption - which one is preferable? -
security - Two takes on PHP two way encryption - which one is preferable? -
i need encrypt info , have decrypted on later point in time. info tied specific users. i've gathered 2 possible solutions...
1: first 1 derived official docs (example #1 @ http://php.net/manual/en/function.mcrypt-encrypt.php):
function encrypt($toencrypt) { global $key; $iv_size = mcrypt_get_iv_size(mcrypt_rijndael_256, mcrypt_mode_cbc); $iv = mcrypt_create_iv($iv_size, mcrypt_rand); homecoming base64_encode($iv . mcrypt_encrypt(mcrypt_rijndael_256, $key, $toencrypt, mcrypt_mode_cbc, $iv)); } function decrypt($todecrypt) { global $key; $iv_size = mcrypt_get_iv_size(mcrypt_rijndael_256, mcrypt_mode_cbc); $todecrypt = base64_decode($todecrypt); homecoming rtrim(mcrypt_decrypt(mcrypt_rijndael_256, $key, substr($todecrypt, $iv_size), mcrypt_mode_cbc, substr($todecrypt, 0, $iv_size))); } the key generated 1 time using:
echo bin2hex(openssl_random_pseudo_bytes(mcrypt_get_iv_size(mcrypt_rijndael_256, mcrypt_mode_cbc))) and later referred this:
$key = pack('h*', [result of above]); 1.1: i've noticed encrypted result ends in 2 equal signs ('=='). why? - using bin2hex() , hex2bin() in encrypt() , decrypt() instead of base64_encode()/base64_decode() respectively not yield these results.
1.2: using bin2hex()/hex2bin() have consequence on outcome (other length)?
1.3: there seems give-and-take whether or not phone call trim-function on homecoming result when decrypting (this applies solution below well). why necessary?
2: sec solution comes here, stackoverflow (php, simplest 2 way encryption):
function encrypt($key, $toencrypt) { homecoming base64_encode(mcrypt_encrypt(mcrypt_rijndael_256, md5($key), $toencrypt, mcrypt_mode_cbc, md5(md5($key)))); } function decrypt($key, $todecrypt) { homecoming rtrim(mcrypt_decrypt(mcrypt_rijndael_256, md5($key), base64_decode($todecrypt), mcrypt_mode_cbc, md5(md5($key))), "\0"); } i'm aware both approaches key handling interchangeable, purposely made them different in respect in order highlight possible solutions, please sense free mix , match.
personally sense first 1 offers tighter security since both key , initialization vector randomized. sec solution however, offer form of non-predictability since key unique each piece of encrypted info (even though suffers under weak randomization of md5()). key illustration user's name.
3: so, 1 preferable? i'm in dark since stackoverflow reply got whopping 105 votes. other thoughts, tips?
4: bonus question!: i'm not incredibly brainy on server security aspects, gaining access php files expose key, direct result, render encryption useless, assuming attacker has access db. there way obscure key?
thank reading , have nice day!
edit: things considered, seems best bet:
function encrypt($toencrypt) { global $key; $iv_size = mcrypt_get_iv_size(mcrypt_rijndael_128, mcrypt_mode_cbc); $iv = mcrypt_create_iv(mcrypt_get_iv_size(mcrypt_rijndael_128, mcrypt_mode_cbc), mcrypt_rand); homecoming base64_encode($iv . mcrypt_encrypt(mcrypt_rijndael_128, $key, $toencrypt, mcrypt_mode_cbc, $iv)); } function decrypt($todecrypt) { global $key; $iv_size = mcrypt_get_iv_size(mcrypt_rijndael_128, mcrypt_mode_cbc); $todecrypt = base64_decode($todecrypt); homecoming rtrim(mcrypt_decrypt(mcrypt_rijndael_128, $key, substr($todecrypt, $iv_size), mcrypt_mode_cbc, substr($todecrypt, 0, $iv_size))); } using key created 1 time using following:
bin2hex(openssl_random_pseudo_bytes(32)));
the main difference between 2 code samples first 1 generates random initialization vector (iv) each message, while sec 1 uses fixed iv derived key.
if never encrypt more 1 message same key, both methods ok. however, encrypting multiple messages same key , iv dangerous, you should never utilize sec code sample encrypt more 1 message same key.
another difference first code sample passes key straight block cipher (rijndael), whereas sec 1 first runs through md5(), apparently in weak effort utilize key derivation function.
if key random bitstring (of suitable length), sample key generation code produce, there's no need run through md5(). if, instead, it's user-provided password, there might advantage hashing — in case, ought utilize proper key derivation function pbkdf2 instead, e.g. this:
$cipher = mcrypt_rijndael_128; // = aes-256 $mode = mcrypt_mode_cbc; $keylen = mcrypt_get_key_size( $cipher, $mode ); $salt = mcrypt_create_iv( $keylen, mcrypt_dev_urandom ); $iterations = 10000; // higher = slower; create high can tolerate $key = hash_pbkdf2( 'sha256', $password, $salt, $iterations, $keylen, true ); note right $salt , $iterations values needed reconstruct key password decryption, remember store them somewhere, e.g. prepending them ciphertext. length of salt doesn't matter much, long it's not short; making equal key length safe plenty choice.
(incidentally, pretty way hash password verify correctness. obviously, shouldn't utilize same $key value both encryption , password verification, safely store, say, hash( 'sha256', $key, true ) alongside ciphertext allow verify password / key correct.)
a few other issues see 2 code snippets:
both snippets utilize mcrypt_rijndael_256, is, apparently, not aes-256, rather non-standard rijndael-256/256 variant, 256-bit block size (and key size). it's probably secure, 256-bit-block-size variants of rijndael have receive much less cryptanalytic scrutiny 128-bit-block-size ones (which standardized aes), you're taking slightly higher risk using them.
thus, if want play safe, need interoperate other software using standard aes, or need able tell boss that, yes, you're using standard nist-approved cipher, should go mcrypt_rijndael_128 (which, apparently, mcrypt calls aes-256) instead.
in key generation code, pack( 'h*', bin2hex( ... ) ) no-op: bin2hex() converts key binary hexadecimal, , pack( 'h*', ... ) reverse. rid of both functions.
also, you're generating key, not iv, should utilize mcrypt_get_key_size(), not mcrypt_get_iv_size(). happens, mcrypt_rijndael_256 there's no difference (since both iv size , key size 32 bytes = 256 bits), mcrypt_rijndael_128 (and many other ciphers) there is.
as owlstead notes, mcrypt's implementation of cbc mode apparently uses non-standard zero-padding scheme. sec code sample correctly removes padding rtrim( $msg, "\0" ); first 1 calls rtrim( $msg ), trim whitespace off end of message.
also, obviously, zero-padding scheme won't work if info can legitimately contain 0 bytes @ end. instead switch other cipher mode, mcrypt_mode_cfb or mcrypt_mode_ofb, not require padding. (out of two, recommend cfb, since accidental iv reuse bad ofb. it's not cfb or cbc either, failure mode much less catastrophic.)
php security encryption cryptography
Comments
Post a Comment