Why has overriding password hashing caused urls to break in magento admin?

356 views Asked by At

I have overriden the hash($data) and validateHash($password, $hash) of Mage_Core_Model_Encryption in a custom module. It works I can login to the admin area with my password and it validates correctly with the new hash; however, I cannot navigate in the admin area. I see a password-like hash becomes part of the url: admin/dashboard/index/key/[bcrypt hash here]. The browser cannot redirect to the resulting url. Can anyone advise me on overriding password hashes without breaking the urls with them? Thanks in advance.

In case it helps, my config xml looks like this:

<?xml version="1.0" ?>
<config>
  <modules>
    <Mycompany_Encryption>
      <version>0.1</version>
      <depends>Mage_Core</depends>
    </Mycompany_Encryption>
  </modules>
  <global>
    <models>
      <core>
        <rewrite>
          <encryption>Mycompany_Encryption_Model_Encryption</encryption>
        </rewrite>
      </core>
    </models>
    <helpers>
      <core>
        <encryption_model>Mycompany_Encryption_Model_Encryption</encryption_model>
      </core>
    </helpers>
  </global>
</config>

UPDATE:: (per two comments below) how to use password_hash instead of md5 for password authentication, while allowing url keys to use the hash function in an url friendly way?

2

There are 2 answers

2
Qoheleth-Tech On BEST ANSWER

The encrypt/decrypt functions of Mage_Core_Model_Encryption are not used in urls.

Basically, there are two ways to solve the conflict. One being to implement the new hash based on using an event observer on the customer authentication methods. The other method, the one which works better for me is to just extend the Mage_Adminhtml_Model_Url and override the getSecretKey method. [The two repos linked in comments we helpful in learning this.]

To fix the issue, I added a node to my config.xml inside models:

<adminhtml>
  <rewrite>
    <url>Mycompany_Encryption_Model_Adminhtml_Url</url>
  </rewrite>
</adminhtml>

And then added the file Mycompany/Encryption/Model/Adminhtml/Url.php:

class Mycompany_Encryption_Model_Adminhtml_Url extends Mage_Adminhtml_Model_Url
{
    public function getSecretKey($controller = null, $action = null)
    {
        $salt = Mage::getSingleton('core/session')->getFormKey();
        $p = explode('/', trim($this->getRequest()->getOriginalPathInfo(), '/'));

        if (!$controller) {
            $controller = !empty($p[1]) ? $p[1] : $this->getRequest()->getControllerName();
        }

        if (!$action) {
             $action = !empty($p[2]) ? $p[2] : $this->getRequest()->getActionName();
        }
        $secret = $controller . $action . $salt;

        // Overriding getHash() to use a diff hash than passwords.
         return Mage::helper('core')->getEncryptor()->urlHash($secret);
    }
}

Lastly, I updated my Mycompany/Encryption/Model/Encryption.php by adding a urlHash method (in addition to the hash, validateHash, and getHash function already located there):

public function urlHash($value)
{
    return md5($value);
}

I could have just returned md5($secret) at the end of getSecretKey and that works also, but this method demonstrates more Mage methods that are helpful to see in action.

I have not fully tested my module yet, but this appears to be fully compatible with every place getSecretKey is called.

0
β.εηοιτ.βε On

I did a little bit of research in the core files and it looks like the way to achieve what you want to achieve is possible via a factory.

Sadly, it looks like Magento did part of the job but not all.

Mage_Core_Model_Encryption::_getCrypt($key = null)

protected function _getCrypt($key = null)
{
    if (!$this->_crypt) {
        if (null === $key) {
            $key = (string)Mage::getConfig()->getNode('global/crypt/key');
        }
        $this->_crypt = Varien_Crypt::factory()->init($key);
    }
    return $this->_crypt;
}

Varien_Crypt which make me said that is not done in full, that is a factory, but it is called with no params in the function above, so, yes, no way to change the object returned by the factory, sigh.

class Varien_Crypt
{
    static public function factory($method='mcrypt')
    {
        $uc = str_replace(' ','_',ucwords(str_replace('_',' ',$method)));
        $className = 'Varien_Crypt_'.$uc;
        return new $className;
    }
}

But then knowing those two are designed like that you could : override Varien_Crypt to have another class instantiated and based on the method of the class Varien_Crypt_Mcrypt, implement the same methods and get what you want to achieve working.

And by looking at this, I also found someone posting on the magento Q&A of stack exchange his module to have a more secure password encryption.
It is under a BSD licence on GitHub so here is his post on the network
and here is the module, called pbkdf2, repository

Maybe you could find something helpful in the way he implemented it (I did not take time to look at his code, tough).