Jump to content

Cracklib password strength checking with ajax


danami

Recommended Posts

This contribution will allow you to add an ajax password strength meter to WHMCS that uses the cracklib library on the server to check password strength. This is the same library that your server and various control panels use for checking passwords.

 

Note: You need to have cracklib, cracklib-dicts, and the PECL PHP cracklib extension installed for this to work!

Make sure that phpinfo() reports crack supported enabled.

Make sure that openbasedir restrictions aren't blocking access to the cracklib dictionary files.

 

1. Save this code to checkpassword.php and place it in your WHMCS directory

<?php
/** 
*    Password check with ajax and cracklib
*       
*    1. Make sure that cracklib is installed on your server.
*    # rpm -qa | grep cracklib
*    # cracklib-2.8.9-3.3
*    # cracklib-dicts-2.8.9-3.3
*
*    2. Make sure that the PHP cracklib extension is installed. Install pecl to install cracklib.
*    # yum install php-pear
*    # pecl install crack
*
*    There is a tiny bug in the compilation procedure, to avoid it do:
*    # cd /var/cache/php-pear/crack-0.4
*    # ./configure; make; make install
*
*    3. Configure php5 to work with crack, edit /etc/php.ini or create a new crack.ini file in /etc/php.d/ and enter:
*    [Crack]
*    ; Modify the setting below to match the directory location of the cracklib
*    ; dictionary files.  Include the base filename, but not the file extension.
*    extension=crack.so
*    crack.default_dictionary = "/usr/share/cracklib/pw_dict"
*   
*    4. Make sure to restart apache. phpinfo(); should report crack support enabled
* 
*    5. crack.default_dictionary files must be accessable to any openbase dir restrictions. 
*    Give openbasedir access to the files or copy the dictionary files to a path that PHP has access to.
*/

/* Our cracklib dictionary file (no file extension) */
define('CRACKLIB_DICTIONARY_FILE','/usr/share/cracklib/pw_dict');

/**
* Check password using PHP's cracklib functions
*
* @param string $password Our password we are checking
* @return string
*/
function checkCracklib($password)
{
// Check if cracklib is installed
if (!function_exists('crack_opendict'))
{
	$message = 'Cracklib is not installed';
	return $message;
}

// Open crackLib dictionary
$dictionary = crack_opendict(CRACKLIB_DICTIONARY_FILE);

if (!$dictionary)
{
	$message = 'Unable to open cracklib dictionary';
	return $message;
}

// Perform cracklib password check
$check = crack_check($dictionary, $password);

// Retrieve messages
$message = crack_getlastmessage();

// Close cracklib dictionary
crack_closedict($dictionary);

return $message;
}

/**
* Generates our xml ajax response
*
* @param string $message Our cracklib message
* @return string
*/
function xmlResponse($message) {

   $output  = '<?xml version="1.0" encoding="UTF-8"?>';
   $output .= '<result><![CDATA[';

   if ($message == 'strong password')
   {  
       $output .= '<div style="width:100%;background:green" id="password_bar">Strong Password</div>';
   }
   else
   {
       $output .= '<div style="width:100%;background:red;" id="password_bar">'.ucfirst($message).'</div>';      
   }

   $output .=']]></result>';

   return $output;
}
$password_test 	= checkCracklib(substr($_GET['pass'],0,128));
$output         = xmlResponse($password_test);
header('Content-Type: text/xml');
header('Pragma: no-cache');
echo $output;
?>

 

 

2. On your password page include javascript and css in your smarty template (for example add to orderforms\default\default\viewcart.tpl)

{literal}
<script type="text/javascript">
function check_password(passid) {
   if (window.XMLHttpRequest) {
       http = new XMLHttpRequest();
   } else if (window.ActiveXObject) {
       http = new ActiveXObject("Microsoft.XMLHTTP");
   }
   handle = document.getElementById(passid);
   var url = 'checkpassword.php?';
   if(handle.value.length > 0) {
       var fullurl = url + 'pass=' + encodeURIComponent(handle.value);
       http.open("GET", fullurl, true);
       http.send(null);
       http.onreadystatechange = statechange_password;
   }else{
       document.getElementById('password_strength').innerHTML = '';
   }
}

function statechange_password() {
   if (http.readyState == 4) {
       var xmlObj = http.responseXML;
       var html = xmlObj.getElementsByTagName('result').item(0).firstChild.data;
       document.getElementById('password_strength').innerHTML = html;
   }
}
</script> 
<style type="text/css">
   #password_strength {
       width: 90%;
       background: #cccccc;
   }
   #password_bar {
       font-size: 9px;
       background: #7FFF00;    
       border: 1px solid #cccccc;
       padding: 2px;
   }
</style>
{/literal}

 

3. Add the password meter html code (for example add to orderforms\default\default\viewcart.tpl)

 

Change

<tr><td class="fieldarea">{$LANG.clientareapassword}</td><td><input type="password" name="password" size="20" /></td></tr>
<tr><td class="fieldarea">{$LANG.clientareaconfirmpassword}</td><td><input type="password" name="password2" size="20" /></td></tr>

 

To

 

<tr><td class="fieldarea">{$LANG.clientareapassword}</td><td><input type="password" name="password" size="20" id="password" onchange="check_password('password')" /></td></tr>
<tr><td class="fieldarea">{$LANG.clientareaconfirmpassword}</td><td><input type="password" name="password2" size="20" /></td></tr>
<tr><td class="fieldarea">Password Strength</td><td><div id="password_strength"></div></td></tr>

 

Now WHMCS will check password strength just like your server does! Have fun!

Edited by danami
Link to comment
Share on other sites

I've updated checkpassword.php to use simple PHP checks first. If you have cracklib enabled then it can use them also.

 

You can enable or disable:

 

Length check

Lowercase check

Uppercase check

Numbers check

Special Characters check

Cracklib check

 

New checkpassword.php

<?php
/**
*    Password check with ajax and cracklib
*       
*    1. Make sure that cracklib is installed on your server.
*    # rpm -qa | grep cracklib
*    # cracklib-2.8.9-3.3
*    # cracklib-dicts-2.8.9-3.3
*
*    2. Make sure that the PHP cracklib extension is installed. Install pecl to install cracklib.
*    # yum install php-pear
*    # pecl install crack
*
*    There is a tiny bug in the compilation procedure, to avoid it do:
*    # cd /var/cache/php-pear/crack-0.4
*    # ./configure; make; make install
*
*    3. Configure php5 to work with crack, edit /etc/php.ini or create a new crack.ini file in /etc/php.d/ and enter:
*    [Crack]
*    ; Modify the setting below to match the directory location of the cracklib
*    ; dictionary files.  Include the base filename, but not the file extension.
*    extension=crack.so
*    crack.default_dictionary = "/usr/share/cracklib/pw_dict"
*   
*    4. Make sure to restart apache. phpinfo(); should report crack support enabled
* 
*    5. crack.default_dictionary files must be accessable to any openbase dir restrictions. 
*    Give openbasedir access to the files or copy the dictionary files to a path that PHP has access to.
*/

/* Normal password checks */
define('PASSWORD_LENGTH_MINIMUM',6); //Set to 0 to disable
define('PASSWORD_LOWERCASE',true);
define('PASSWORD_UPPERCASE',true);
define('PASSWORD_NUMBERS',true);
define('PASSWORD_SPECIAL_CHARS',true);

/* Enable our cracklib dictionary check and define path to dictionary file (no file extension) */
define('CRACKLIB_ENABLED',false);
define('CRACKLIB_DICTIONARY_FILE','/usr/share/cracklib/pw_dict');

/**
* Check password using PHP's cracklib functions
*
* @param string $password Our password we are checking
* @return string
*/
function checkCracklib($password)
{
   // Check if cracklib is installed
   if (!function_exists('crack_opendict'))
   {
       return 'Cracklib is not installed';
   }

   // Open crackLib dictionary
   $dictionary = crack_opendict(CRACKLIB_DICTIONARY_FILE);

   if (!$dictionary)
   {
       return 'Unable to open cracklib dictionary';
   }

   // Perform cracklib password check
   $check = crack_check($dictionary, $password);

   // Retrieve messages
   $message = crack_getlastmessage();

   // Close cracklib dictionary
   crack_closedict($dictionary);

   return $message;
}

/**
* Check password using simple tests
* @param string $password Our password we are checking
* @return string
*/
function checkPassword($password)
{
   // Password Length
   if(PASSWORD_LENGTH_MINIMUM !=0 && (strlen($password) < PASSWORD_LENGTH_MINIMUM))
   {
       return 'It must be at least '.PASSWORD_LENGTH_MINIMUM.' characters in length';
   }
   // Letters - lowercase
   if(PASSWORD_LOWERCASE && !preg_match("/([a-z]+)/", $password))
   {
       return 'It must contain lowercase letters';
   }
   // Letters - uppercase
   if(PASSWORD_UPPERCASE && !preg_match("/([A-Z]+)/", $password))
   {
       return 'It must contain uppercase letters';
   }
   // Numbers
   if(PASSWORD_NUMBERS && !preg_match("/([0-9]+)/", $password))
   {
       return 'It must contain numbers';
   }

   // Special Characters
   if(PASSWORD_SPECIAL_CHARS && !preg_match("/[\W_]/" , $password))
   {
       return 'It must contain special characters';
   }

   return 'strong password';
}

/**
* Generates our xml ajax response
*
* @param string $message Our cracklib message
* @return string
*/
function xmlResponse($message)
{
   $output  = '<?xml version="1.0" encoding="UTF-8"?>';
   $output .= '<result><![CDATA[';

   if ($message == 'strong password')
   {
       $output .= '<div style="width:100%;background:green" id="password_bar">Strong Password</div>';
   }
   else
   {
       $output .= '<div style="width:100%;background:red;" id="password_bar">'.ucfirst($message).'</div>';
   }

   $output .=']]></result>';

   return $output;
}

//Check password
$password       = substr($_GET['pass'],0,128);
$password_test  = checkPassword($password);

//If password passes normal test check using cracklib dictionary
if ($password_test == 'strong password' && CRACKLIB_ENABLED)
{
   $password_test 	= checkCracklib($password);
}

$output = xmlResponse($password_test);
header('Content-Type: text/xml');
header('Pragma: no-cache');
echo $output;
?> 

Edited by danami
Link to comment
Share on other sites

yum install php-pear

did not work

 

Those instructions are for rpm based distros (Redhat/Centos) . If you are using debian or another OS then you'll need to try and install it yourself.

 

If you can't get cracklib installed .. then just use the basic checks included in the second checkpassword.php file ...

Edited by danami
Link to comment
Share on other sites

* extension=crack.so

* crack.default_dictionary = "/usr/share/cracklib/pw_dict"

 

phpinfo did nto show cracklib support. searched for "crack" in the phpinfo page, but its not found.

 

Did you do all the steps outlined in the file to get crack installed (These instructions are for rpm based distros only)?

 

1. Make sure that cracklib is installed on your server.

 

rpm -qa | grep cracklib

cracklib-2.8.9-3.3

cracklib-dicts-2.8.9-3.3

 

2. Make sure that the PHP cracklib extension is installed. Install pecl to install cracklib.

 

yum install php-pear

pecl install crack

 

There is a tiny bug in the compilation procedure, to avoid it do:

 

cd /var/cache/php-pear/crack-0.4

./configure; make; make install

 

3. Configure php5 to work with crack, edit /etc/php.ini or create a new crack.ini file in /etc/php.d/ and enter:

 

[Crack]

; Modify the setting below to match the directory location of the cracklib

; dictionary files. Include the base filename, but not the file extension.

extension=crack.so

crack.default_dictionary = "/usr/share/cracklib/pw_dict"

Link to comment
Share on other sites

If you want to require strong passwords you can add this javascript function to the ajax javascript clear any weak passwords that are submitted (add this to the original ajax javascript functions).

 

function empty_weak_password(passid, passid2) {

   var html = document.getElementById('password_strength').innerHTML;    
   var pos  = html.indexOf("Strong Password");

   if (pos < 0) {
       document.getElementById(passid).value  = '';
       document.getElementById(passid2).value = '';
   }
}

 

then change the form onsubmit to this (for example add to orderforms\default\default\viewcart.tpl):

 

<form method="post" action="{$smarty.server.PHP_SELF}?a=checkout" {if !$loggedin} onsubmit="empty_weak_password('password','password2')" {/if}>

 

Then just change the "Password required" lang in your language file to "A strong password is required"

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated