UPDATE (SOLUTION)
Since this post seems to get decent amount of attention, I'd like to let you know that the solution ended up being to provide a proper enctype (content type) parameter in the <FORM> declaration. You must set the value to multipart/form-data to prevent encoding that would otherwise take place using the default enctype of application/x-www-form-urlencoded. A small excerpt below from Forms in HTML Documents at w3.org:
The content type
"application/x-www-form-urlencoded" is
inefficient for sending large
quantities of binary data or text
containing non-ASCII characters. The
content type "multipart/form-data"
should be used for submitting forms
that contain files, non-ASCII data,
and binary data.
And here is the proper FORM declaration:
<FORM method="POST" action="/path/to/file/" name="encryptedForm" enctype="multipart/form-data">
INITIAL QUESTION
I am working on a form spam protection class which essentially replaces form field names with an encrypted value using mcrypt. The problem with this is that mcrypt encryption is not limited to only alphanumeric characters which would invalidate form fields. Given the code below, can you think of any reason why I'd be having problems decrypting the values of the already encrypted array?
/**
* Two way encryption function to encrypt/decrypt keys with
* the DES encryption algorithm.
*/
public static function encryption($text, $encrypt = true)
{
$encrypted_data = '';
$td = mcrypt_module_open('des', '', 'ecb', '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
if (mcrypt_generic_init($td, substr(self::$randomizer, 16, 8), $iv) != -1) {
if ($encrypt) {
// attempt to sanitize encryption for use as a form element name
$encrypted_data = mcrypt_generic($td, $text);
$encrypted_data = base64_encode($encrypted_data);
$encrypted_data = 'i' . strtr($encrypted_data, '+/=', '-_.');
self::$encrypted[] = $encrypted_data;
} else {
// reverse form element name sanitization and decrypt
$text = substr($text, 1);
$text = strtr($text, '-_.', '+/=');
$text = base64_decode($text);
$encrypted_data = mdecrypt_generic($td, $text);
}
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
return $encrypted_data;
}
I later make a call setting a hidden form element's value using:
base64_encode(serialize(self::$encrypted))
Essentially the hidden field contains an array of all form fields which were encrypted with their encrypted value. This is so I know which fields need to be decrypted on the backend. Upon form submission this field gets parsed on the backend with the following code:
// load the mapping entry
$encrypted_fields = $input->post('encrypted', '');
if (empty($encrypted_fields)) {
throw new AppException('The encrypted form field was empty.');
}
// decompress array of encrypted fields
$encrypted_fields = #unserialize(base64_decode($encrypted_fields));
if ($encrypted_fields === false) {
throw new AppException('The encrypted form field was not valid.');
}
// get the mapping of encrypted keys to key
$data = array();
foreach ($_POST as $key => $val) {
// if the key is encrypted, add to data array decrypted
if (in_array($key, $encrypted_fields)) {
$decrypted = self::encryption($key, false);
$data[$decrypted] = $val;
unset($_POST[$key]);
} else {
$data[$key] = $val;
}
}
// merge $_POST array with decrypted key array
$_POST += $data;
My attempts to decrypt the encrypted form field keys are failing. It's simply creating a new garbled key in the $_POST array. My guess is that either base64_encoding or serialization is stripping chars from the $encrypted_data. Could somebody verify if this is the culprit and whether there are any alternative methods for encoding form keys?
So I took your code, and modified it a little so that I can remove the element of a post request and your function seems to work fine. If you take the code I posted and create a script with it, it should run in the cli and you'll see its encrypting/decrypting the fields correctly. This would have to mean that the post request is some how garbling the encrypted/serialized/encoded data. If using a framework, I would look more into how it handles the post array as it could altering your keys/values causing them to not match up. The code that you posted seems fine.
<?php
/**
* Two way encryption function to encrypt/decrypt keys with
* the DES encryption algorithm.
*/
function encryption($text, $encrypt = true, &$encryptedFields = array())
{
$encrypted_data = '';
$td = mcrypt_module_open('des', '', 'ecb', '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
if (mcrypt_generic_init($td, substr('sdf234d45)()*5gf512/?>:LPIJ*&U%&^%NBVFYUT^5hfhgvkjtIUUYRYT', 16, 8), $iv) != -1) {
if ($encrypt) {
// attempt to sanitize encryption for use as a form element name
$encrypted_data = mcrypt_generic($td, $text);
$encrypted_data = base64_encode($encrypted_data);
$encrypted_data = 'i' . strtr($encrypted_data, '+/=', '-_.');
//self::$encrypted[] = $encrypted_data;
$encryptedFields[] = $encrypted_data;
} else {
// reverse form element name sanitization and decrypt
$text = substr($text, 1);
$text = strtr($text, '-_.', '+/=');
$text = base64_decode($text);
$encrypted_data = mdecrypt_generic($td, $text);
}
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
}
return $encrypted_data;
}
$encryptedFields = array();
// encrypt some form fields
encryption('firstname', true, $encryptedFields);
encryption('lastname', true, $encryptedFields);
encryption('email_fields', true, $encryptedFields);
echo "Encrypted field names:\n";
print_r($encryptedFields);
// create a usable string of the encrypted form fields
$hiddenFieldStr = base64_encode(serialize($encryptedFields));
echo "\n\nFull string for hidden field: \n";
echo $hiddenFieldStr . "\n\n";
$encPostFields = unserialize(base64_decode($hiddenFieldStr));
echo "\n\nDecrypted field names:\n";
foreach($encPostFields as $field)
{
echo encryption($field, false)."\n";
}
?>
Related
I want to pass some variable to the URL using PHP $_GET['']; for example:
I have a form on a landing page with five input fields: Name, Surname, Email, Confirm-Email, Phone I pick these variables up now I want to add these to a Base URL so that they can be picked up by another web page.
My question is how safe is this and what is the best method to protect these variables or perhaps make them invisible under the new url...?
I could use php curl() or sockets but the server where I want to send the data to does not allow me to so thats why I want to use $_GET['']'
You should not use $_GET if you want to make your data secure.
There is no option using which you can make $_GET secure or invisible.
You should use $_POST for sending data to other url if you do not want to make your data visible and secure.
Even your data is not stored by browser when you use post method and it is more difficult to hack.
It's unsafe, as URI parameters can be edited by anyone and anything. Best method to "protect" those is to encrypt them when sending (which means the recipient must be able to decrypt them) combined with a checksum to make sure none of the parameters were altered. Another type of protection is not using HTTP but using HTTPS instead, of course.
Both GET and POST is unsafe if you're not using HTTPS.
So if you have to use GET to submit your form, a verify token is suggeseted. Like OAuth, server will return the data through GET, but there's a access_token to protect data.
Encode your variables first, then decode it on the other server. this way no one can easily revert it. Make sure to change var $skey = "SecretKey0001"; to something else.
<?php
class Encryption {
var $skey = "SecretKey0001"; // you can change it
public function safe_b64encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='),array('-','_',''),$data);
return $data;
}
public function safe_b64decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
public function encode($value){
if(!$value){return false;}
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->skey, $text, MCRYPT_MODE_ECB, $iv);
return trim($this->safe_b64encode($crypttext));
}
public function decode($value){
if(!$value){return false;}
$crypttext = $this->safe_b64decode($value);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->skey, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
}
$data_array['Name'] = 'Name';
$data_array['Surname'] = 'Surname';
$data_array['Email'] = 'Email';
$data_array['Confirm-Email'] = 'Confirm-Email';
$data_array['Phone'] = 'Phone';
$data_json = json_encode($data_array);
$encrypt = new Encryption;
$encoded_vars = $encrypt->encode($data_json);
$BASE_URL = 'http://example.com?data=' . $encoded_vars;
echo $BASE_URL;
echo "<br>";
// reverse
$decrypt = new Encryption;
echo $decoded_vars = $decrypt->decode($encoded_vars);
echo "<br>";
$data = json_decode(urldecode($decoded_vars), true);
echo "<br>";
print_r($data);
?>
DEMO: http://sandbox.onlinephpfunctions.com/code/ef1acdcad0272d5d99e21b07183a479f564ac64c
First, excuse the question, it might be a simple problem, but I have troubles understanding the encryption methods..
I'm using the following functions to encrypt / decrypt:
private function encodemc($value,$skey){
if(!$value){return false;}
$skey = substr($skey, 2, 4);
$text = $value;
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $skey, $text, MCRYPT_MODE_ECB, $iv);
return trim($this->safe_encode($crypttext)); // safe_encode adds another encoding using `base64_encode`
}
private function decodemc($value,$skey){
if(!$value){return false;}
$skey = substr($skey, 2, 4);
$crypttext = $this->safe_decode($value);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$decrypttext = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $skey, $crypttext, MCRYPT_MODE_ECB, $iv);
return trim($decrypttext);
}
The $key looks like this: 570c45546dwq45gjk191.
I pass the value to be encrypted to the first function, then I save it to the db, and then I retrieve it from the db, I decrypt it and show it as html text.
The problem is that some text does not get decrypted/encrypted right, and it displays in the html page as if it was in the wrong text encoding.
The weird part is that out of 10 items, only 2 or 3 are garbled, depending on the key.
In addition, sometimes only a portion of the string is garbled.
I've found out that what causes the garbling are some random letters. For instance, when using the above key the letter S breaks the code and the text gets garbled.
So then I've applied substr($skey, 2, 4); to the key to see if anything changed. Turns out that if I change the key the characters that break the code are different.
But even with a key of lenght == 1 the problem persists.
Any idea on what's the problem?
EDIT:
Here the rest of the code.
private function safe_encode($string) {
$data = base64_encode($string);
$data = str_replace(array('+','/','='),array('-','_',''),$data);
return $data;
}
private function safe_decode($string) {
$data = str_replace(array('-','_'),array('+','/'),$string);
$mod4 = strlen($data) % 4;
if ($mod4) {
$data .= substr('====', $mod4);
}
return stripslashes(base64_decode($data));
}
That seems like a very weird issue.
I do not have a definite answer for you, but here are a couple of things you can try.
Encrypt/Decrypt without going to the database. If the issues goes away, then there is a problem with the database handling the characters output though the encryption function.
Trim the value before you encrypt it. If the issues goes away, then the trim after encryption is wrong.
Remove the safe_encode and safe_decode code. If the issue goes away, then these functions are adding/removing important things.
It is important that you do all the above in one go, as there might be multiple issues. If the above steps remove the issues, then reintroduce them one at a time to identify which is causing the problem.
I am new to AES but from what I have found there are several modes (ECB,CBC, etc.) and different modes need different initialization vector requirements, blocks, and encodings. I am trying to decode the following
Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9xHivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZbf3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CAkjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDsCd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=
using php and the (text) key "043j9fmd38jrr4dnej3FD11111111111" with mode CBC and an IV of all zeros. I am able to get it to work with this tool but can't get it in php. Here is the code I am using:
function decrypt_data($data, $iv, $key) {
$data = base64_decode($data);
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
I think I may be missing something relating to base 64 encoding or turning the key into binary first. I have tried decoding many things and all I can produce is gibberish. Any help would be very appreciated.
Well the tool itself does not say how exactly it's encrypted. And you can't set the IV either so it's hard to get the parameters right (because they have to be equal).
After some guesswork I found out the following:
The IV is prepended to the ciphertext
The ciphertext is encrypted with aes-128-cbc
So you have to modify the code:
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
if(is_null($iv)) {
$ivlen = mcrypt_enc_get_iv_size($cypher);
$iv = substr($data, 0, $ivlen);
$data = substr($data, $ivlen);
}
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
$ctext = "Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9x" .
"HivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZb" .
"f3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CA" .
"kjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDs" .
"Cd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=";
$key = "043j9fmd38jrr4dnej3FD11111111111";
$res = decrypt_data(base64_decode($ctext), null, $key);
I'm not sure why the key length is not used to encrypt it with aes-256-cbc - I've checked out the source of that as3crypto-library and it kind of supported it, but I would have to debug it to really verify it.
Openfire stores encrypted passwords in a database using blowfish encryption.
http://svn.igniterealtime.org/svn/repos/openfire/trunk/src/java/org/jivesoftware/util/Blowfish.java is the java implementation for how encrypt / decrypt functions work in openfire.
My goal is to create new user entries in the database via PHP and MySQLI. All of the variations I've tried have yielded results that don't match what already exists in the database. For example:
d3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db is one of the encrypted passwords. clear text, this is stackoverflow
I've tried a few variations:
echo mcrypt_cbc(MCRYPT_BLOWFISH, '1uY40SR771HkdDG', 'stackoverflow', MCRYPT_ENCRYPT, '12345678');
// result: áë*sY¶nŸÉX_33ô
Another based on mcrypt blowfish php slightly different results when compared to java and .net
$key = '1uY40SR771HkdDG';
$pass = 'stackoverflow';
$blocksize = mcrypt_get_block_size('blowfish', 'cbc'); // get block size
$pkcs = $blocksize - (strlen($data) % $blocksize); // get pkcs5 pad length
$data.= str_repeat(chr($pkcs), $pkcs); // append pkcs5 padding to the data
// encrypt and encode
$res = base64_encode(mcrypt_cbc(MCRYPT_BLOWFISH,$key, $pass, MCRYPT_ENCRYPT));
echo $res;
// result: 3WXKASjk35sI1+XJ7htOGw==
Any clever ideas, or any glaring problems? I simply want to implement Blowfish.encryptString() as referenced in the first link in this question.
Here's a class I made, it encrypts and decrypts properly.
Note, you need to save / [pre/app]end the IV in order to reproduce results.
Some test vectors for the java code would be nice.
<?php
/**
* Emulate OpenFire Blowfish Class
*/
class OpenFireBlowfish
{
private $key;
private $cipher;
function __construct($pass)
{
$this->cipher = mcrypt_module_open('blowfish','','cbc','');
$this->key = pack('H*',sha1($pass));
}
function encryptString($plaintext, $iv = '')
{
if ($iv == '') {
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this->cipher));
}
else {
$iv = pack("H*", $iv);
}
mcrypt_generic_init($this->cipher, $this->key, $iv);
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$plaintext = mb_convert_encoding($plaintext,'UTF-16BE'); // set to 2 byte, network order
$pkcs = $bs - (strlen($plaintext) % $bs); // get pkcs5 pad length
$pkcs = str_repeat(chr($pkcs), $pkcs); // create padding string
$plaintext = $plaintext.$pkcs; // append pkcs5 padding to the data
$result = mcrypt_generic($this->cipher, $plaintext);
mcrypt_generic_deinit($this->cipher);
return $iv.$result;
}
function decryptString($ciphertext)
{
$bs = mcrypt_enc_get_block_size($this->cipher); // get block size
$iv_size = mcrypt_enc_get_iv_size($this->cipher);
if ((strlen($ciphertext) % $bs) != 0) { // check string is proper size
return false;
}
$iv = substr($ciphertext, 0, $iv_size); // retrieve IV
$ciphertext = substr($ciphertext, $iv_size);
mcrypt_generic_init($this->cipher, $this->key, $iv);
$result = mdecrypt_generic($this->cipher, $ciphertext); // decrypt
$padding = ord(substr($result,-1)); // retrieve padding
$result = substr($result,0,$padding * -1); // and remove it
mcrypt_generic_deinit($this->cipher);
return $result;
}
function __destruct()
{
mcrypt_module_close($this->cipher);
}
}
$enckey = "1uY40SR771HkdDG";
$enciv = 'd3f499857b40ac45';
$javastring = 'd3f499857b40ac45c41828ccaa5ee1f90b19ca4e0560d1e2dcf4a305f219a4a2342aa7364e9950db';
$a = new OpenFireBlowfish($enckey);
$encstring = bin2hex($a->encryptString('stackoverflow',$enciv));
echo $encstring . "\n";
echo $a->decryptString(pack("H*", $encstring)) . "\n";
$b = new OpenFireBlowfish($enckey);
echo $b->decryptString(pack("H*", $javastring)) . "\n";
There is nothing wrong with your code, however to generate the same code as Openfire, you will need to add in two other items before the encrypted text.
length of ciphertext
CBCIV (initialization variable)
Read "public String decryptString(String sCipherText)" in java code, it's all there. Also check the docs on how to use CBCIV in PHP.
Openfire's code prepends the CBCIV passed with the output string. It also using Unicode as the character set. These together may be the problem area.
I don't know enough about Blowfish's internals to help more, sorry.
I'm working on a little script that will allow me to store relatively secure information in a cookie to validate a user login without the use of sessions. Part of the output is an encrypted salt to use when generating a hmac_hash with some of the information stored in the cookie, and some of the user information in the database.
However, after some testing, I've ran into a problem with the encryption/decryption of the strings and causing different hash results.
ie:
$str = '123456abcdef';
$hash1 = sha1($str);
$v1 = do_encrypt($str);
$v2 = do_decrypt($v1);
$hash2 = sha1($v2);
and I end up with
$hash1 - d4fbef92af33c1789d9130384a56737d181cc6df
$hash2 - 0d6034f417c2cfe1d60d263101dc0f8354a1216f
but when I echo both strings, they are both 123456abcdef.
The do_encrypt function is as follows:
function do_encrypt($value) {
$salt = generate_salt();
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
mcrypt_generic_init($td, $ek, $salt);
$encrypted_data = mcrypt_generic($td, $value);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($salt.$encrypted_data);
}
The do_decrypt function:
function do_decrypt($value) {
$data = base64_decode($value);
$salt = substr($data, 0, 32);
$data = substr($data, 32, strlen($data));
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
mcrypt_generic_init($td, $ek, $salt);
$decrypted_data = mdecrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $decrypted_data;
}
for both functions $ek is an encryption key pulled from another file.
I'm trying to understand why the characters that display are the same, but the actual variables are different (otherwise the hash results would be the same), and is there any way to ensure that both strings are identical for hashing purposes?
Thanks,
Ryan.
As per comments, it looks like you are getting trailing nulls - It's likely that mcrypt has a block size of 32 bytes and that any encrypted/decrypted string must be a multiple of this many bytes.
Taken from the mcrypt_encrypt documentation:
If the size of the data is not n * blocksize, the data will be padded with '\0'.