Skip to content

Instantly share code, notes, and snippets.

@Shudrum
Last active April 9, 2017 20:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Shudrum/27fc534de69c96617280 to your computer and use it in GitHub Desktop.
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!

@cedricfontaine
Copy link

For the Customer, I think that the changes should be done in PasswordController and also in classes/Customer for the Transformtocustomer function.

@stratboy
Copy link

Hi, in adminlogincontroller / initContent() line 87, there's another reference to passwdgen:

$rand = 'admin'.sprintf('%03d', rand(0, 999)).Tools::strtolower(Tools::passwdGen(6)).'/';

Should it be patched too?

Also, please correct the customer mod explanation: the method is not processForgot, it's transformToCustomer instead.

Finally, please add explanation for the PasswordController

Thank you

@davidalegria
Copy link

Can I replace manually the 1.4.11.0 patched files in a PS 1.4.8.2 installation?
Or is mandatory upgrade to the version PS 1.4.11.0 to replace?
I have no changes in the Core.

@Xaconi
Copy link

Xaconi commented Aug 10, 2015

HI there, there is a little error in this explanation (at the "Do it for yourself" section). The passwdGen() method, now must return the $result variable (aside from the other changes). If we keep it like how it's now, the return variable ($password) is empty, and it causes errors on the messages system and in the references generator.

@IeMdev
Copy link

IeMdev commented Aug 10, 2015

V1.6.0.10
Have been able to update it all except I can not find 'processForgot()' in the Customer Class to update it.
Is the update in '/classes/Customer.php' or another file.

@JennyOFWB
Copy link

Hi!! I am new for PS and using 1.6.09. there is a error while am uploading security patch. "Password generation update
Improved algorithm for password generation. " So where i have to change the code and which files. Can anyone suggest me.

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Hi @Nasshat, sorry for the long time.

This is exactly what you have to do for fix the breach by yourself. Do not forget to backup the files before :) Just in case !

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Hi @cedricfontaine and @stratboy !

Thank you, we will take a look at it as soon as possible !

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Hi @davidalegria,

I can't say yes right now. I'm pretty sure the files have changed between these two versions.
Try to do it by yourself !

It is really simple !
🍰

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Hi @IeMdev,

Really sorry, I've made a mistake, this mehod (processForgot) is ont the controllers/admin/AdminLoginController.php class.

I'll fix it.

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Ho @JennyOFWB,

Just follow the "Do It Yourself" section :)

@Shudrum
Copy link
Author

Shudrum commented Aug 10, 2015

Hi @Xaconi,

I don't understand, if you look here :
PrestaShop/PrestaShop-1.5@f1ef8aa?diff=split&w=1

The result is always returned.

@shkurkin
Copy link

Thanks for the writeup!

I am modifying a 1.5.6.2 installation and I think there is an error in the DIY section, step 3. corrections below:

III. Modify the customer password generation call

Now find your PasswordController, and the postProcess() method,

@JennyOFWB
Copy link

Hi @Shudrum,

Thanks, I have follow the "Do it Yourself" Section and i have changed the code but still am getting the same error. :(

@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