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!

@Shudrum
Copy link
Author

Shudrum commented Aug 12, 2015

Hi @shkurkin,

Thank you for your feedback, there is some errors on my gist :( I've made a change on the wrong section ><
I'll fix this as fast as possible.

Sorry !

@Shudrum
Copy link
Author

Shudrum commented Aug 12, 2015

Hi @JennyOFWB,

You have made the changes ? Great ! So you do not have to install the module !

@LeRameur
Copy link

Still not getting it. I am comfortable changing code in files. I have done and still do that with our installation of 4images. It is how all the modifications are made.

Our prestashop store is 1.5.5.0

What are our choices? Can we just follow the cut and replace steps shown in this link:
PrestaShop/PrestaShop-1.5@f1ef8aa?w=1
?

Or do we need to upgrade our installation to 1.5.6.2? We have made some changes to the core.

Thanks,

Brian

@Shudrum
Copy link
Author

Shudrum commented Aug 13, 2015

Hi @LeRameur,

You can just follow the steps on the commit !

@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