Skip to content

Commit

Permalink
Faster CSRF & CSRF Expiry
Browse files Browse the repository at this point in the history
  • Loading branch information
bajb committed May 16, 2020
1 parent ebb0d0b commit 35e3ba1
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 10 deletions.
17 changes: 12 additions & 5 deletions src/Csrf/CsrfDataHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
use Packaged\Form\DataHandlers\AbstractDataHandler;
use Packaged\Form\Decorators\HiddenInputDecorator;
use Packaged\Form\Decorators\Interfaces\DataHandlerDecorator;
use function password_hash;
use const PASSWORD_DEFAULT;

class CsrfDataHandler extends AbstractDataHandler
{
Expand All @@ -14,10 +12,14 @@ class CsrfDataHandler extends AbstractDataHandler
protected $_formSecret;
protected $_value;

public function __construct($formSecret, $sessionSecret)
//Total number of minutes this csrf will be valid for, null for unlimited time
protected $_expiryMins;

public function __construct($formSecret, $sessionSecret, ?int $expiryMinutes)
{
$this->setFormSecret($formSecret);
$this->setSessionSecret($sessionSecret);
$this->_expiryMins = $expiryMinutes;
}

public function applyNewToken()
Expand All @@ -30,11 +32,16 @@ public function getDefaultValue()
{
if($this->_defaultValue === null)
{
$this->_defaultValue = password_hash($this->_generatePassword(), PASSWORD_DEFAULT);
$this->_defaultValue = static::generateHash($this->_generatePassword(), $this->_expiryMins === null ? 0 : time());
}
return $this->_defaultValue;
}

public static function generateHash(string $password, $timestamp = 0): string
{
return md5($password . $timestamp) . base_convert($timestamp, 10, 36);
}

protected function _generatePassword()
{
return $this->_getFormSecret() . $this->_getSessionSecret();
Expand Down Expand Up @@ -80,7 +87,7 @@ public function setSessionSecret($sessionSecret)

protected function _setupValidator()
{
$this->addValidator(new CsrfValidator($this->_generatePassword()));
$this->addValidator(new CsrfValidator($this->_generatePassword(), $this->_expiryMins));
}

protected function _defaultDecorator(): DataHandlerDecorator
Expand Down
5 changes: 4 additions & 1 deletion src/Csrf/CsrfForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class CsrfForm extends Form

protected $_sessionSecret;

//Number of minutes the csrf token should be valid for. Setting to null for unlimited time
protected $_tokenExpiryMinutes = 60;

public function __construct($sessionSecret)
{
$this->_sessionSecret = $sessionSecret;
Expand All @@ -21,7 +24,7 @@ public function __construct($sessionSecret)

protected function _initDataHandlers()
{
$this->csrfToken = new CsrfDataHandler($this->_getCsrfSecret(), $this->_sessionSecret);
$this->csrfToken = new CsrfDataHandler($this->_getCsrfSecret(), $this->_sessionSecret, $this->_tokenExpiryMinutes);
}

protected function _getCsrfSecret()
Expand Down
15 changes: 11 additions & 4 deletions src/Csrf/CsrfValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,29 @@
use Generator;
use Packaged\Validate\AbstractValidator;
use Packaged\Validate\ValidationException;
use function password_verify;

class CsrfValidator extends AbstractValidator
{
protected $_password;
protected $_expiryMinutes;

public function __construct(string $password)
public function __construct(string $password, ?int $expiryMinutes)
{
$this->_password = $password;
$this->_expiryMinutes = $expiryMinutes;
}

protected function _doValidate($value): Generator
{
if(!password_verify($this->_password, $value))
$timestamp = base_convert(substr($value, 32), 36, 10);
if($this->_expiryMinutes !== null && $timestamp < time() - ($this->_expiryMinutes * 60))
{
yield new ValidationException('invalid or missing CSRF token');
yield new ValidationException('Anti-Forgery token expired');
}

if(CsrfDataHandler::generateHash($this->_password, $timestamp) !== $value)
{
yield new ValidationException('Anti-Forgery token missing or invalid');
}
}
}
5 changes: 5 additions & 0 deletions tests/Csrf/CsrfFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public function testCsrfToken()
$form->csrfToken->applyNewToken();
$formValue = $form->csrfToken->getValue();

$this->assertTrue($form->csrfToken->isValid());
$this->assertTrue($form->csrfToken->isValidValue($formValue));

$form->csrfToken->applyNewToken();
$formValue = $form->csrfToken->getValue();
$this->assertTrue($form->csrfToken->isValid());
$this->assertTrue($form->csrfToken->isValidValue($formValue));
$form->csrfToken = $formValue;
Expand Down

0 comments on commit 35e3ba1

Please sign in to comment.