I'm trying to encrypt user-input which is first validated and sanitized by PHP. The data then gets encrypted using openssl and is stored in a MYSQL-backend. Over time we want to download that table and decrypt it with Python. So I'm looking at a cross-language requirement.
I have the following string as user input:
someone.somebody@blabla.com
this is sanitized using PHP's builtin method:
filter_var($verifiedMail, FILTER_SANITIZE_EMAIL)
I then pass it to the encryption logic of the PHP script:
$lock = "82YoH8jE7TJyEX2hLzFe35wTPaaDgVP1"; // use a 256 bit key!!!!!
/*Ideally you'd define the $lock as defined constant and store it elsewhere!!!*/
$method = 'aes-256-cfb';
$ivlen = openssl_cipher_iv_length($method);
$isSecure = false;
do{
$iv = openssl_random_pseudo_bytes($ivlen,$isSecure);
}while(!isSecure);
$mailStorage = openssl_encrypt($mail, $method, $lock, 0, $iv);
unset($method, $lock, $ivlen);
In the SQL database I store two variables: $iv
and $mailStorage
For this iteration these values are:
$iv = '1abcd16944cc06c09e1d1635108a9077';
$mailStorage = 'gRDzkzHiysRqfjNXAYY+TWJ+88LtUcWfPFkn';
My $iv
variable gets stored as a hexadecimal value by calling bin2hex($iv)
when inserting the value in the database. So far so good; (I guess?)
The values are in the database, and the PHP-script works without throwing errors. So I continued to work on the Python decryption part. The relevant block for this is:
import binascii
from Crypto.Cipher import AES
from base64 import b64decode
securedoutput = "gRDzkzHiysRqfjNXAYY+TWJ+88LtUcWfPFkn" #the result of the encryption done by php
vector = "1abcd16944cc06c09e1d1635108a9077"# the IV generated by PHP and stored as hex in the database
password = "82YoH8jE7TJyEX2hLzFe35wTPaaDgVP1"##the key used for encryption
iv = binascii.unhexlify(vector)
password = password.encode()
cipher = AES.new(password, AES.MODE_CFB, iv)
securedoutput = b64decode(securedoutput)
data = cipher.decrypt(securedoutput)
print(data)
print(data.decode())
I start by converting the hexadecimal initialization vector back to binary; and I encode the password which seems to be needed by the python module.
When I print out the data
-variable I get:
b'sBD\xec@\x90\xbe\xe3\xe6\xccdv\x13\xb8\xb06@\x1a\xe6r\x82\xdfwe\xb0\xf4\x80'
and when applying data.decode()
I get:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xec in position 3: invalid continuation byte
My main question is: Why is my python output in now way matching the PHP-input; what am I doing wrong when encrypting and/or decrypting the string?
Another question - more related to defensive programming principles relates to this PHP-block:
$isSecure = false;
do{
$iv = openssl_random_pseudo_bytes($ivlen,$isSecure);
}while(!isSecure);
should I add a limit to this while-loop; or can I assume that openssl isn't going to take 100 iterations to find a secure value for $iv
? What are some considerations I should make for this?
Finally; cryptography is a completely new field for me and I've spent the past few days reading up on initialization vectors, encryption modes etc... To the best of my limited knowledge, this should be secure; or am I blatantly wrong? I did not apply padding as I was led to believe that the CFB
mode doesn't require that. Assuming my PHP-script doesn't leak; what's the worst that could happen if the database leaks (so the iv
and encrypted output
) are out in the open?
I've been looking at other questions which seem to use outdated modules such as:
mcrypt: Decrypting strings in Python that were encrypted with MCRYPT_RIJNDAEL_256 in PHP
They helped me in understanding the problem, but apparently not enough.