Skip to content

Instantly share code, notes, and snippets.

@Shudrum
Last active April 9, 2017 20:32
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Shudrum/27fc534de69c96617280 to your computer and use it in GitHub Desktop.

#Password security fix

There are several ways to implement the latest security update for PrestaShop. Choose the one you feel confident with!

  • For Git user: Cherry-pick
  • For Unix user: Apply the patch with diff files
  • For others: Copy the patched files
  • For those who like a challenge: The ‘Do it Yourself’ method!

For other situations (old versions of PrestaShop, heavily customized Core files), you should do it yourself! If you have a little bit of PHP experience, it is easy to do.

##Cherry-picking the commits

If you use Git, you can just cherry-pick the commits for the right version.

###1.6.0.14

For this version, there are two commits to use:

###1.5.6.2

For version 1.5.6.2, there is only one commit to cherry-pick: f1ef8aa913ea013c42d9f2d702142caf031b4945

###1.4.11.0

For version 1.4.11.0, use this one commit for the cherry-pick: 44b811cdd1266a31e8f6754f6fd5c1b10bdef690

##Using the diff files

If you are a Linux/Unix user, you can use patch to modify the files. Only use this if you know what you are doing!

##Using the patched files

Here are the zip files for each version:

The admin folder name can differ from yours: make sure you rename it with the right folder name! Be careful, if you have made some modifications on some of theses files, you will lose them. In this case, instead of copying the whole files, you should modify each files as explained in the ‘Do it yourself!’ section.

##Do it yourself!

The modification to apply is easy to understand:

Look at the PrestaShop 1.5.6.2 commit here.

Follow these steps:

###Modify the Tools password generation

I. Modify the Tools passwdGen method

The method passwdGen() will take two arguments : $length and $flag. Older versions only take one argument ($length). The code should do a switch() from this flag: if there is no switch on older versions, just add it.

Your method will now have something like this:

switch ($flag) {
    case 'NUMERIC':
        $str = '0123456789';
        break;
    case 'NO_NUMERIC':
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        break;
    case 'RANDOM':
        $num_bytes = ceil($length * 0.75);
        $bytes = Tools::getBytes($num_bytes);
        return substr(rtrim(base64_encode($bytes), '='), 0, $length);
    case 'ALPHANUMERIC':
    default:
        $str = 'abcdefghijkmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    break;
}

Then the password generation just below should look like this:

$bytes = Tools::getBytes($length);
$position = 0;
$result = '';

for ($i = 0; $i < $length; $i++) {
    $position = ($position + ord($bytes[$i])) % strlen($str);
    $result .= $str[$position];
}

Perfect, there is now a new method called getBytes($length). Just add it! Copy/paste this:

public static function getBytes($length)
{
    $length = (int)$length;
    
    if ($length <= 0) {
        return false;
    }
    
    if (function_exists('openssl_random_pseudo_bytes')) {
        $bytes = openssl_random_pseudo_bytes($length, $crypto_strong);
        if ($crypto_strong === true) {
            return $bytes;
        }
    }

    if (function_exists('mcrypt_create_iv')) {
        $bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
        if ($bytes !== false && strlen($bytes) === $length) {
            return $bytes;
        }
    }

    // Else try to get $length bytes of entropy
    $result         = '';
    $entropy        = '';
    $msec_per_round = 400;
    $bits_per_round = 2;
    $total          = $length;
    $hash_length    = 20;
    while (strlen($result) < $length) {
        $bytes  = ($total > $hash_length) ? $hash_length : $total;
        $total -= $bytes;
        for ($i=1; $i < 3; $i++) {
            $t1 = microtime(true);
            $seed = mt_rand();
            for ($j=1; $j < 50; $j++) {
                $seed = sha1($seed);
            }
            $t2 = microtime(true);
            $entropy .= $t1 . $t2;
        }
        $div = (int) (($t2 - $t1) * 1000000);
        if ($div <= 0) {
            $div = 400;
        }
        $rounds = (int) ($msec_per_round * 50 / $div);
        $iter = $bytes * (int) (ceil(8 / $bits_per_round));
        for ($i = 0; $i < $iter; $i ++) {
            $t1 = microtime();
            $seed = sha1(mt_rand());
            for ($j = 0; $j < $rounds; $j++) {
                $seed = sha1($seed);
            }
            $t2 = microtime();
            $entropy .= $t1 . $t2;
        }
        $result .= sha1($entropy, true);
    }
    return substr($result, 0, $length);
}

Wonderful, the hardest part is done!

II. [For 1.4 versions only] Change the password validity

This version is using a regex to check the password validity. Replace it with the following method on the Validate class:

public static function isPasswd($passwd, $size = 5)
{
 return (strlen($passwd) >= $size && strlen($passwd) < 255);
}

III. Modify the customer password generation call

Now find your AdminLoginController, and the processForgot() method, which is called when the user wants to get a new password.

It should contain a call to our Tools method like this:

$password = Tools::passwdGen(MIN_PASSWD_LENGTH)

Just replace it with this one:

$password = Tools::passwdGen(MIN_PASSWD_LENGTH, 'RANDOM')

IV. Modify the admin password generation call

The final step is the call from the administrators (same as for the customer).

There was an admin controller named AdminLoginController or, on older versions, a file named password.php.

Find the process called (on the controller) processForgot() and the call to our Tools method:

$pwd = Tools::passwdGen();

And just replace it with:

$pwd = Tools::passwdGen(10, 'RANDOM');

It is done, you did it!

@asgllccb
Copy link

I'm in the exact situation as @Nasshat and followed the instructions you gave him using the 1.5.6.2 commit example noted at the beginning because they seemed more clear/obvious to me. After I made the changes in the files and uploaded them to my site, my admin login page no longer worked. The page was just blank. Fortunately I just changed the original files to end in 1 before the .php and when it didn't work I added a 0 to the end of my updated files and took out the 1 on the originals and everything is working fine again, but of course I don't have the fixes. Was it because the example was for 1.5x rather than the 1.6.0.8 that I have? Also tried to update site to .14 version so we could apply the patch, but neither the update nor the patch seemed to work, so I was really glad to find this page. Please advise as to how I can get this to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment