Zend_Form Validator: Konfigurierbare Vorgaben für Passwörter erzwingen

Hier ein Zend Form Validator der dazu genutzt werden kann sichere(re) Passwörter zu erzwingen. Die exakten Anforderungen können hierbei direkt in der Hauptkonfiguration “application.ini” angepasst werden. Hintergrund zur Idee war dass ich in der Entwicklungsumgebung gerne einfache Passwörter zum testen nutzen wollte, im Livebetrieb aber natürlich höhere Ansprüche an den Keyspace gestellt werden.

Einbinden kann man ihn in Zend_Form z.B. wie folgt:

class App_Form_User extends Zend_Form
{
public function init()
{
//
// prepare validator
$secPasswdValidator = new Custom_Validate_SecurePassword();

//
// password field
$this->addElement('password', 'password', array(
'filters'      => array('StringTrim'),
'required'     => true,
'validators'   => array(
$secPasswdValidator,
array('StringLength', false, array(0,128))
),
'label'        => 'Passwort',
'size'         => 12,
'autocomplete' => 'off'
));
}
}

Damit Zend die Klasse findet, muss “Custom” in application.ini in den autoloaderNamespace eingefügt werden:

autoloaderNamespaces[] = "Custom"

Konfiguriert wird er in application.ini wie folgt. Die Werte können in der development Sektion überschrieben werden:

[production]
user.password.minLength = 8
user.password.minDigits = 2
user.password.minUpperChars = 1
user.password.minLowerChars = 1
user.password.minSpecialChars = 1
user.password.specialChars = "!-_%"

Nun noch der Validator selbst. Abgelegt würde er z.B. unter library/Custom/Validate/SecurePassword.php

< ?php

class Custom_Validate_SecurePassword extends Zend_Validate_Abstract
{
const LENGTH_VIOLATION = 'length';
const DIGITS_VIOLATION = 'digits';
const UPPER_VIOLATION = 'upper';
const LOWER_VIOLATION = 'lower';
const SPECIALCHAR_VIOLATION = 'specialchars';

/**
*
* @var array
*/
protected $_messageTemplates = null;

/**
* password specification
*
* @var array
*/
protected $_pwConfig = null;

public function  __construct()
{
// read password policy from config
try
{
$config = Zend_Registry::get('config');
$this->_pwConfig = $config->user->password;

$this->_messageTemplates = array(
self::LENGTH_VIOLATION => "Passwort muss min. "
.$this->_pwConfig->minLength
." Zeichen enthalten",

self::DIGITS_VIOLATION => "Passwort muss min. "
.$this->_pwConfig->minDigits
." Ziffern enthalten",

self::UPPER_VIOLATION => "Passwort muss min. "
.$this->_pwConfig->minUpperChars
." Großbuchstaben enthalten",

self::LOWER_VIOLATION => "Passwort muss min. "
.$this->_pwConfig->minLowerChars
." Kleinbuchstaben enthalten",

self::SPECIALCHAR_VIOLATION => "Passwort muss min. "
.$this->_pwConfig->minSpecialChars
." dieser Sonderzeichen enthalten: "
.$this->_pwConfig->specialChars
);
}
catch (Zend_Exception $e)
{
throw $e;
}
}

/**
*
* @param strong $value
* @param string|array $context
*/
public function isValid($value, $context = null)
{
// search entities
$foundDigits = 0;
$foundUpperChars = 0;
$foundLowerChars = 0;
$foundSpecialChars = 0;

$specialChars = preg_quote($this->_pwConfig->specialChars);
$valueLength = strlen($value);

for ($i = 0; $i < $valueLength; $i++)
{
if (preg_match('/^\d{1}$/', $value[$i]))
{
$foundDigits++;
}
else if (preg_match('/^[A-Z]$/', $value[$i]))
{
$foundUpperChars++;
}
else if (preg_match('/^[a-z]$/', $value[$i]))
{
$foundLowerChars++;
}
else if (preg_match('/^['.$specialChars.']$/', $value[$i]))
{
$foundSpecialChars++;
}
}

if ($valueLength < $this->_pwConfig->minLength)
{
$this->_error(self::LENGTH_VIOLATION);
return false;
}
else if ($foundDigits < $this->_pwConfig->minDigits)
{
$this->_error(self::DIGITS_VIOLATION);
return false;
}
else if ($foundUpperChars < $this->_pwConfig->minUpperChars)
{
$this->_error(self::UPPER_VIOLATION);
return false;
}
else if ($foundLowerChars < $this->_pwConfig->minLowerChars)
{
$this->_error(self::LOWER_VIOLATION);
return false;
}
else if ($foundLowerChars < $this->_pwConfig->minLowerChars)        {            $this->_error(self::LOWER_VIOLATION);            return false;        }
else if ($foundSpecialChars < $this->_pwConfig->minSpecialChars)
{
$this->_error(self::SPECIALCHAR_VIOLATION);
return false;
}

// passed all checks successful!
return true;
}
}
?>

Jegliches Feedback hierzu ist natürlich mehr als willkommen. =)