Viewed   810 times

Mcrypt function has been deprecated as of PHP 7.1.0.

My deprecated string encode / decode functions:

$key: secret key
$str: string


$encoded = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $str, MCRYPT_MODE_CBC, md5(md5($key))));

$decoded = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($str), MCRYPT_MODE_CBC, md5(md5($key))), "");

Can you suggest some alternatives?

 Answers

5

You should use openssl_encrypt instead.

Sunday, November 13, 2022
4

Thanks to @LawrenceCherone help, I did find out that you need to "output data" for connection_aborted to work...

In my case I was outputing data only when I needed to ...

by adding

   if ($hasChange) {
      //Output stuffs
      echo 'data:';
      echo json_encode($result);
      echo "nn";
      ob_flush();
      flush();
      sleep(5);

    } else {
       echo 'data:';
       echo "nn";
       ob_flush();
       flush();
       if(connection_aborted()){
         exit();
       }
       sleep(5);
    }

connection_aborted started working.

Wednesday, October 5, 2022
 
bridge
 
3

Easy peasy lemon squeezy (no).
Yes, creation of password protected archives is not supported (they will be created simply as non-protected archives, as you just described).
But, still it can be used to extract password protected archives.

Returning to the problem.
You always can just

<?php echo system('zip -P pass file.zip file.txt'); ?>

(this will work both on Windows and our beloved Linux)

But, if it not fits into your requirements, let's continue.
I would suggest you to use DotNetZip (Windows only), you will exactly dynamically generate AES-encrypted zip archives from PHP.

<?php
// origin: https://.com/a/670804/3684575
try
{
  $fname = "zip-generated-from-php-" . date('Y-m-d-His') . ".zip";
  $zipOutput = "c:\temp\" . $fname;
  $zipfact = new COM("Ionic.Zip.ZipFile");
  $zip->Name = $zipOutput;
  $dirToZip= "c:\temp\psh";
  # Encryption:  3 => 256-bit AES.  
  #     2 => 128-bit AES.  
  #     1 => PKZIP (Weak).  
  #     0 => None
  $zip->Encryption = 3;
  $zip->Password = "AES-Encryption-Is-Secure";
  $zip->AddDirectory($dirToZip);
  $zip->Save();
  $zip->Dispose();

  if (file_exists($zipOutput))
  {
    header('Cache-Control: no-cache, must-revalidate');
    header('Content-Type: application/x-zip'); 
    header('Content-Disposition: attachment; filename=' . $fname);
    header('Content-Length: ' . filesize($zipOutput));
    readfile($zipOutput);
    unlink($zipOutput);
  }
  else 
  {
    echo '<html>';
    echo '  <head>';
    echo '  <title>Calling DotNetZip from PHP through COM</title>';
    echo '  <link rel="stylesheet" href="basic.css"/>';
    echo '  </head>';
    echo '<body>';
    echo '<h2>Whoops!</h2>' . "<br/>n";
    echo '<p>The file was not successfully generated.</p>';
    echo '</body>';
    echo '</html>';
  } 
}
catch (Exception $e) 
{
    echo '<html>';
    echo '  <head>';
    echo '  <title>Calling DotNetZip from PHP through COM</title>';
    echo '  <link rel="stylesheet" href="basic.css"/>';
    echo '  </head>';
    echo '<body>';
    echo '<h2>Whoops!</h2>' . "<br/>n";
    echo '<p>The file was not successfully generated.</p>';
    echo '<p>Caught exception: ',  $e->getMessage(), '</p>', "n";
    echo '<pre>';
    echo $e->getTraceAsString(), "n";
    echo '</pre>';
    echo '</body>';
    echo '</html>';
}

?>

But still, this is very dirty solution and more of that, not works on Linux.

So, although PHP is a mature language, there is no adequate method (excluding custom extension or something like that) to achieve such a simple task with pure PHP.
What you also can do, is to wait until PHP 7.2 will be available for production (cuz ZipArchive::setEncryptionName is implemented (thanks to Pierre and Remi)).
But, until then you also can try to port php_zip >= 1.14.0 to PHP < 7.2, but there is currently no compiled binaries available, so you have to compile it yourself and try if it is possible at all (I believe it is).
p.s. I would try it, but have no VS2015+ on my PC right now.

Wednesday, October 5, 2022
 
5

It's best practice to hash passwords so they are not decryptable. This makes things slightly more difficult for attackers that may have gained access to your database or files.

If you must encrypt your data and have it decryptable, a guide to secure encryption/decryption is available at https://paragonie.com/white-paper/2015-secure-php-data-encryption. To summarize that link:

  • Use Libsodium - A PHP extension
  • If you can't use Libsodium, use defuse/php-encryption - Straight PHP code
  • If you can't use Libsodium or defuse/php-encryption, use OpenSSL - A lot of servers will already have this installed. If not, it can be compiled with --with-openssl[=DIR]
Sunday, November 20, 2022
 
2

The main issue appears to be that your string_encrypt and string_decrypt PHP functions don't have access to the $key variable, so for the encryption key mcrypt_encrypt is using . See this question for an explanation. PHP should report a notice that key is undefined, have you turned off error reporting perhaps? Echo the key from inside the encrypt function to confirm this.

Another issue is a bug in the Mcrypt JS library. This library pads the encryption key with if the key length is less than 32 bytes, the problem is that this is not how the PHP mcrypt_encrypt function pads the key. The mcrypt_encrypt function pads the key up to the nearest valid key length (16, 24, or 32 bytes). The issue in mcrypt.js is at lines 63 and 64, change this:

if(key.length<32)
    key+=Array(33-key.length).join(String.fromCharCode(0));

to this:

if(key.length<16)
    key+=Array(17-key.length).join(String.fromCharCode(0));
else if(key.length<24 && key.length>16)
    key+=Array(25-key.length).join(String.fromCharCode(0));
else if(key.length<32 && key.length>24)
    key+=Array(33-key.length).join(String.fromCharCode(0));

Now we can confirm the fix...

PHP:

function string_encrypt($string) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, "", $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

$test_str = "This is test message to be encrypted.";
$enc_str = string_encrypt($test_str);
echo bin2hex($enc_str);

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e

Javascript:

function toHex(str) {
    var hex = '';
    for(var i=0;i<str.length;i++) {
        var val = ''+str.charCodeAt(i).toString(16);
        if(val.length == 1)
            hex += '0'+val;
        else
            hex += val;
    }
    return hex;
}

var enc_str = mcrypt.Encrypt("This is test message to be encrypted.", "", "", "rijndael-256", "ecb");
alert(toHex(enc_str));

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e

Finally, all of these encryption functions produce binary as their output. Binary cannot be written as plain text in most cases without damaging the data. To solve this, either encode the binary to Hex or Base64 and then decode it before trying to decrypt.

So to get everything working...

<?php 
$key = 'testtesttesttesttesttesttesttest';

function string_encrypt($string, $key) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

function string_decrypt($encrypted_string, $key) {
    $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_string, MCRYPT_MODE_ECB);
    return trim($decrypted_text);
}

echo $test_str = 'This is test message to be encrypted.';   echo '<br />';
$enc_str = string_encrypt($test_str, $key);
echo bin2hex($enc_str);                                     echo '<br />';
echo string_decrypt($enc_str, $key);                        echo '<br />';

?>

<script src='rijndael.js'></script>
<script src='mcrypt.js'></script>

<script lang='javascript'>
    function toHex(str) {
        var hex = '';
        for(var i=0;i<str.length;i++) {
            var val = ''+str.charCodeAt(i).toString(16);
            if(val.length == 1)
                hex += '0'+val;
            else
                hex += val;
        }
        return hex;
    }
    function hexToString (hex) {
        var str = '';
        for (var i=0; i<hex.length; i+=2) {
            str += ''+String.fromCharCode(parseInt(hex.charAt(i)+hex.charAt(i+1), 16));
        }
        return str;
    }
    var enc_str = mcrypt.Encrypt('<?php echo $test_str ?>', '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb');
    alert(toHex(enc_str));
    alert(mcrypt.Decrypt(hexToString('<?php echo bin2Hex($enc_str) ?>'), '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb').replace(/x00+$/g, '')); 
</script>

A few more notes...

  1. You cannot trim the output of the string_encrypt function. This will cause leading or trailing zeros to be removed, which will make it so that you cannot decrypt the output.
  2. ECB mode is insecure and you really shouldn't use it. CBC is the way to go. CBC does require an IV, and the IV must be the same for both encryption and decryption.
  3. Javascript encryption is not secure for various reasons, given your usage of it anyone could simply view the pages source or debug the running javascript to get the encryption key. Read the link posted by ntoskrnl in your question comments.

Update:

Your Base64 encoding issue occurs because the library you're using doesn't work with binary data. This is a fairly common issue for Base64 javascript libraries. I'd recommend using this library instead.

As for the trailing ? characters when decrypting with javascript, you need to trim the decrypted output. You're doing this in your PHP string_decrypt method, but not in your javascript. You can trim the decrypted output by doing a regex replace on all characters at the end of the string.

Example:

mcrypt.Decrypt(dec_str,'').replace(/x00+$/g, '')

I should have included this in my original post, but I didn't notice the characters in the output because FF's alert box doesn't display them. Sorry about that.

Finally, I noticed another bug in the Mcrypt JS library. Lines 41 to 47:

var ciphers={       //  block size, key size
    "rijndael-128"  :[  16,         32],
    "rijndael-192"  :[  24,         32],
    "rijndael-256"  :[  32,         32],
    "serpent"       :[  16,         32],
    "twofish"       :[  16,         32],
}

Notice the comma at the end of the "twofish" line. Firefox and Chrome don't seem to mind this, but IE8 will report an error and fail to load the mcrypt library because of it. To fix the issue change:

"twofish"       :[  16,         32],

to:

"twofish"       :[  16,         32]
Tuesday, October 11, 2022
 
jbalogh
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :