Security QA for Forgotten Password

Last modified by bloggleme on Fri, June 25, 2010 09:49
Source|Old Revisions  

This is an old revision of the document!


Have you ever noticed that some websites require you to supply them with a security question and answer for your account? If you forget your password, you are required to provide those credentials in order to retrieve a new password. This protects customer information and ensures that the original creator of the account will always have a way of getting their account back (if their account had been hacked/stolen).

In this tutorial, I’m going to explain how to add those two fields to the registration page and require them to be provided on the forgot password page - where they the information will be validated.

Please open the following files, as we will be working with them: /app/code/core/Mage/Customer/controllers/AccountController.php - This controls all of the functions that we need to edit. /app/design/frontend/default/yourtheme/template/forgotpassword.phtml - This is the template file for the forgot password page. /app/design/frontend/default/yourtheme/template/customer/form/register.phtml - this is the template file for the register page.

Alright, first things first. We will start by adding the Security Question and Answer fields to the registration page. We will also have to create a new attribute for the two fields.

At the top of register.phtml, add the following code:

  1. <?php
  2. $setup = new Mage_Eav_Model_Entity_Setup('core_setup');
  3. // Set up the Security Question Attribute
  4. $AttrCode = 'squestion';
  5. $settings = array (
  6.     'position' => 1,
  7.     'is_required'=> 1
  8. );
  9. // Set up the Security Answer Attribute
  10. $AttrCode2 = 'sanswer';
  11. $settings2 = array (
  12.     'position' => 1,
  13.     'is_required'=> 1
  14. );
  15. // Adds both attributes
  16. $setup->addAttribute('1', $AttrCode, $settings);
  17. $setup->addAttribute('1', $AttrCode2, $settings2);
  18. ?>

Now, navigate your browser to http://www.yoursite.com/customer/account/create/ By navigating to this page, you have executed the php code we just added to the register.phtml file. Your attributes are now added. Please remove or comment out the code we previously added to the register.phtml file (it is no longer needed).

In register.phtml, navigate near Line 58. You should see this line of code:

  1.         </ul>

Immediately after this line, add the following block of code:

  1. <br/><br/>Please type in your security question/answer for retrieving your password.<br/>
  2.     <ul>
  3.         <li>
  4.                 <div class="input-box">
  5.                     <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
  6.                     <select id="squestion" name="squestion" title="<?php echo $this->__('Security Question') ?>" class="required-entry validate-select">
  7.             <option value="What is the name of your dog?">What is the name of your dog?</option>
  8.                         <option value="What is the name of your favorite teacher?">What is the name of your favorite teacher?</option>
  9.             <option value="What is your maiden name?">What is your maiden name?</option>
  10.             <option value="In what city were you born?">In what city were you born?</option>
  11.             <option value="What is your favorite food?">What is your favorite food?</option>
  12.                     </select>
  13.                 </div>
  14.                 <div class="input-box">
  15.                     <label for="sanswer"><?php echo $this->__('Security Answer') ?> <span class="required">*</span></label><br />
  16.                     <input type="text" name="sanswer" id="sanswer" value="<?php echo $this->htmlEscape($this->getFormData()->getSecurityAnswer()) ?>" title="<?php echo $this->__('Security Answer') ?>" class="required-entry input-text" />
  17.                 </div>
  18.             </li>
  19.         </ul>

Now, in your forgotpassword.phtml file, find this block of code (around line 25):

  1. <fieldset>
  2.                 <legend><?php echo $this->__('Retrieve your password here') ?></legend>
  3.         <p><?php echo $this->__('Please enter your email below and we'll send you a new password.') ?></p>
  4.         <ul class="form-list">
  5.             <li>
  6.                 <div class="input-box"><label for="email_address"><?php echo $this->__('Email Address') ?> <span class="required">*</span></label><br/>
  7.                 <input name="email" alt="email" type="text" id="email_address" class="required-entry validate-email input-text" value="<?php echo $this->htmlEscape($this->getEmailValue()) ?>" /></div></li>
  8.         </ul>
  9.     <div class="button-set">
  10.         <p class="required"><?php echo $this->__('* Required Fields') ?></p>
  11.         <a href="<?php echo $this->helper('customer')->getLoginUrl() ?>" class="left">&laquo; <?php echo $this->__('Back to Login') ?></a>
  12.         <button type="submit" class="form-button right"><span><?php echo $this->__('Submit') ?></span></button>
  13.     </div>
  14.         </fieldset>

Replace this block of code with this:

  1. <fieldset>
  2.                 <legend><?php echo $this->__('Retrieve your password here') ?></legend>
  3.         <p><?php echo $this->__('Please enter your email below and we'll send you a new password.') ?></p>
  4.         <ul class="form-list">
  5.             <li>
  6.                 <div class="input-box"><label for="email_address"><?php echo $this->__('Email Address') ?> <span class="required">*</span></label><br/>
  7.                 <input name="email" alt="email" type="text" id="email_address" class="required-entry validate-email input-text" value="<?php echo $this->htmlEscape($this->getEmailValue()) ?>" /></div>
  8.         </li>
  9.         <li>
  10.         <div class="input-box">
  11.         <?
  12.         if(strlen($this->htmlEscape($this->getSquestionValue()))>0){
  13.         ?>
  14.         <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
  15.         <input name="squestion" alt="squestion" type="text" id="squestion" class="required-entry input-text" style="border:0; background:none; font-weight:bold;" value="<?php echo $this->htmlEscape($this->getSquestionValue()) ?>" readonly /></div>
  16.                 <? } else { ?>
  17.         <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
  18.                     <select id="squestion" name="squestion" title="<?php echo $this->__('Security Question') ?>" class="required-entry validate-select">
  19.             <option value="What is the name of your dog?">What is the name of your dog?</option>
  20.                         <option value="What is the name of your favorite teacher?">What is the name of your favorite teacher?</option>
  21.             <option value="What is your maiden name?">What is your maiden name?</option>
  22.             <option value="In what city were you born?">In what city were you born?</option>
  23.             <option value="What is your favorite food?">What is your favorite food?</option>
  24.                     </select>
  25.         </div>
  26.         <? } ?>
  27.         </li>
  28.         <li>
  29.         <div class="input-box">
  30.         <label for="sanswer"><?php echo $this->__('Security Answer') ?> <span class="required">*</span></label><br />
  31.         <input name="sanswer" alt="sanswer" type="text" id="sanswer" class="required-entry input-text" /></div>
  32.         </div>
  33.         </li>
  34.         </ul>
  35.     <div class="button-set">
  36.         <p class="required"><?php echo $this->__('* Required Fields') ?></p>
  37.         <a href="<?php echo $this->helper('customer')->getLoginUrl() ?>" class="left">&laquo; <?php echo $this->__('Back to Login') ?></a>
  38.         <button type="submit" class="form-button right"><span><?php echo $this->__('Submit') ?></span></button>
  39.     </div>
  40.         </fieldset>

Alright, your template files are finished. Now we need to edit the AccountController.php code. Find this block of code:

  1. public function loginAction()
  2.     {
  3.         if ($this->_getSession()->isLoggedIn()) {
  4.             $this->_redirect('*/*/');
  5.             return;
  6.         }
  7.         $this->getResponse()->setHeader('Login-Required', 'true');
  8.         $this->loadLayout();
  9.         $this->_initLayoutMessages('customer/session');
  10.         $this->_initLayoutMessages('catalog/session');
  11.         $this->renderLayout();
  12.     }

Add this line of code immediately before “$this→getResponse()→setHeader(’Login-Required’, ‘true’);”

  1. if(!$this->_getSession()->getUsername()){$this->_getSession()->unsForgottenEmail();}

Now, find this block of code within the loginPostAction function:

  1. if (!$session->login($login['username'], $login['password'])) {
  2.                     $session->addError($this->__('Invalid login or password'));
  3.                     $session->setUsername($login['username']);
  4.                 }

Replace it with this:

  1. if (!$session->login($login['username'], $login['password'])) {
  2.                     $session->addError($this->__('Invalid login or password'));
  3.                     $session->setUsername($login['username']);
  4.                     $session->setForgottenEmail($login['username']);
  5.                 }

Now, find this block of code within the createPostAction function:

  1. $customer = Mage::getModel('customer/customer')
  2.                 ->setFirstname($this->getRequest()->getPost('firstname'))
  3.                 ->setLastname($this->getRequest()->getPost('lastname'))
  4.                 ->setEmail($this->getRequest()->getPost('email'))
  5.                 ->setPassword($this->getRequest()->getPost('password'))
  6.                 ->setConfirmation($this->getRequest()->getPost('confirmation'))
  7.                 ->setId(null);

Add these lines immediately after the line containing setEmail:

  1. ->setSquestion($this->getRequest()->getPost('squestion'))
  2.                 ->setSanswer($this->getRequest()->getPost('sanswer'))

Now we need to edit the forgotPasswordAction() function. So find this block of code:

  1. public function forgotPasswordAction()
  2.     {
  3.         $this->loadLayout();
  4.  
  5.         $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
  6.             $this->_getSession()->getForgottenEmail()
  7.         );
  8.         $this->_getSession()->unsForgottenEmail();
  9.  
  10.         $this->_initLayoutMessages('customer/session');
  11.         $this->renderLayout();
  12.     }

And replace it with this:

  1. public function forgotPasswordAction()
  2.     {
  3.         $this->loadLayout();
  4.  
  5.         $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
  6.             $this->_getSession()->getForgottenEmail()
  7.         );
  8.  
  9.         if((strlen($this->_getSession()->getForgottenEmail())>0)){
  10.             $customer = Mage::getModel('customer/customer')
  11.                     ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
  12.                     ->loadByEmail($this->_getSession()->getForgottenEmail());
  13.             //$this->_getSession()->unsForgottenEmail();
  14.             if ($customer->getId()) {
  15.                 $this->getLayout()->getBlock('forgotPassword')->setSquestionValue(
  16.                     $customer->getSquestion()
  17.                 );
  18.             } else {
  19.                 $this->_getSession()->addError($this->__('This email address was not found in our records'));
  20.             }
  21.         }
  22.  
  23.         $this->_initLayoutMessages('customer/session');
  24.         $this->renderLayout();
  25.     }

Alright, finally we can edit the forgotPasswordPostAction() function. Find this large block of code:

  1. public function forgotPasswordAction()
  2.     {
  3.         $this->loadLayout();
  4.  
  5.         $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
  6.             $this->_getSession()->getForgottenEmail()
  7.         );
  8.         $this->_getSession()->unsForgottenEmail();
  9.  
  10.         $this->_initLayoutMessages('customer/session');
  11.         $this->renderLayout();
  12.     }
  13.  
  14.     /**
  15.      * Forgot customer password action
  16.      */
  17.     public function forgotPasswordPostAction()
  18.     {
  19.         $email = $this->getRequest()->getPost('email');
  20.         if ($email) {
  21.             if (!Zend_Validate::is($email, 'EmailAddress')) {
  22.                 $this->_getSession()->setForgottenEmail($email);
  23.                 $this->_getSession()->addError($this->__('Invalid email address'));
  24.                 $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  25.                 return;
  26.             }
  27.             $customer = Mage::getModel('customer/customer')
  28.                 ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
  29.                 ->loadByEmail($email);
  30.  
  31.             if ($customer->getId()) {
  32.                 try {
  33.                     $newPassword = $customer->generatePassword();
  34.                     $customer->changePassword($newPassword, false);
  35.                     $customer->sendPasswordReminderEmail();
  36.  
  37.                     $this->_getSession()->addSuccess($this->__('A new password was sent'));
  38.  
  39.                     $this->getResponse()->setRedirect(Mage::getUrl('*/*'));
  40.                     return;
  41.                 }
  42.                 catch (Exception $e){
  43.                     $this->_getSession()->addError($e->getMessage());
  44.                 }
  45.             }
  46.             else {
  47.                 $this->_getSession()->addError($this->__('This email address was not found in our records'));
  48.                 $this->_getSession()->setForgottenEmail($email);
  49.             }
  50.         } else {
  51.             $this->_getSession()->addError($this->__('Please enter your email.'));
  52.             $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  53.             return;
  54.         }
  55.  
  56.         $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  57.     }

And replace it with this:

  1. public function forgotPasswordPostAction()
  2.     {
  3.         $email = $this->getRequest()->getPost('email');
  4.         $squestion = $this->getRequest()->getPost('squestion');
  5.         $sanswer = $this->getRequest()->getPost('sanswer');
  6.         if ($email) {
  7.             if (!Zend_Validate::is($email, 'EmailAddress')) {
  8.                 $this->_getSession()->setForgottenEmail($email);
  9.                 $this->_getSession()->addError($this->__('Invalid email address'));
  10.                 $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  11.                 return;
  12.             }
  13.             $customer = Mage::getModel('customer/customer')
  14.                 ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
  15.                 ->loadByEmail($email);
  16.  
  17.             if ($customer->getId()) {
  18.                 if(($customer->getSquestion()==$squestion)&&($customer->getSanswer()==$sanswer)) {
  19.                     try {
  20.                         $newPassword = $customer->generatePassword();
  21.                         $customer->changePassword($newPassword, false);
  22.                         $customer->sendPasswordReminderEmail();
  23.  
  24.                         $this->_getSession()->addSuccess($this->__('A new password was sent'));
  25.  
  26.                         $this->getResponse()->setRedirect(Mage::getUrl('*/*'));
  27.                         return;
  28.                     }
  29.                     catch (Exception $e){
  30.                         $this->_getSession()->addError($e->getMessage());
  31.                     }
  32.                 } else {
  33.                     $this->_getSession()->addError($this->__('Your security Q&A credentials were incorrect.'));
  34.                     $this->_getSession()->setForgottenEmail($email);
  35.                 }
  36.             }
  37.             else {
  38.                 $this->_getSession()->addError($this->__('This email address was not found in our records'));
  39.                 $this->_getSession()->setForgottenEmail($email);
  40.             }
  41.         } else {
  42.             $this->_getSession()->addError($this->__('Please enter your email.'));
  43.             $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  44.             return;
  45.         }
  46.  
  47.         $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
  48.     }

Alright, now that it is finished, I’ll explain the full functionality. If you navigate to your account login page and the login fails, you will be redirected to the same page telling you that your password/username was incorrect. Now, click on the Forgot Password link that should already be there. You will notice that the username and security question will automatically be filled in for the username you attempted to login to. From here, put in your security answer and your password will be sent to you. If you type in the wrong security answer, you will get a notice saying your security credentials were incorrect.

Let me know if you have any questions or suggestions for improvement!

Thanks,

Chris Woodard




 

Magento 2 GitHub Repository

Magento Job Board - Some sort of tag line goes here

Latest Posts| View all Jobs