Move token generation to Security namespace
This commit is contained in:
@@ -5,7 +5,7 @@ namespace Kanboard\Auth;
|
|||||||
use Kanboard\Core\Base;
|
use Kanboard\Core\Base;
|
||||||
use Kanboard\Core\Request;
|
use Kanboard\Core\Request;
|
||||||
use Kanboard\Event\AuthEvent;
|
use Kanboard\Event\AuthEvent;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RememberMe model
|
* RememberMe model
|
||||||
@@ -165,8 +165,8 @@ class RememberMe extends Base
|
|||||||
*/
|
*/
|
||||||
public function create($user_id, $ip, $user_agent)
|
public function create($user_id, $ip, $user_agent)
|
||||||
{
|
{
|
||||||
$token = hash('sha256', $user_id.$user_agent.$ip.Security::generateToken());
|
$token = hash('sha256', $user_id.$user_agent.$ip.Token::getToken());
|
||||||
$sequence = Security::generateToken();
|
$sequence = Token::getToken();
|
||||||
$expiration = time() + self::EXPIRATION;
|
$expiration = time() + self::EXPIRATION;
|
||||||
|
|
||||||
$this->cleanup($user_id);
|
$this->cleanup($user_id);
|
||||||
@@ -216,7 +216,7 @@ class RememberMe extends Base
|
|||||||
*/
|
*/
|
||||||
public function update($token)
|
public function update($token)
|
||||||
{
|
{
|
||||||
$new_sequence = Security::generateToken();
|
$new_sequence = Token::getToken();
|
||||||
|
|
||||||
$this->db
|
$this->db
|
||||||
->table(self::TABLE)
|
->table(self::TABLE)
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
namespace Kanboard\Controller;
|
namespace Kanboard\Controller;
|
||||||
|
|
||||||
use Pimple\Container;
|
use Pimple\Container;
|
||||||
use Kanboard\Core\Security;
|
|
||||||
use Kanboard\Core\Request;
|
|
||||||
use Kanboard\Core\Response;
|
|
||||||
use Symfony\Component\EventDispatcher\Event;
|
use Symfony\Component\EventDispatcher\Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,22 +13,6 @@ use Symfony\Component\EventDispatcher\Event;
|
|||||||
*/
|
*/
|
||||||
abstract class Base extends \Kanboard\Core\Base
|
abstract class Base extends \Kanboard\Core\Base
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Request instance
|
|
||||||
*
|
|
||||||
* @accesss protected
|
|
||||||
* @var \Kanboard\Core\Request
|
|
||||||
*/
|
|
||||||
protected $request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Response instance
|
|
||||||
*
|
|
||||||
* @accesss protected
|
|
||||||
* @var \Kanboard\Core\Response
|
|
||||||
*/
|
|
||||||
protected $response;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@@ -41,11 +22,9 @@ abstract class Base extends \Kanboard\Core\Base
|
|||||||
public function __construct(Container $container)
|
public function __construct(Container $container)
|
||||||
{
|
{
|
||||||
$this->container = $container;
|
$this->container = $container;
|
||||||
$this->request = new Request;
|
|
||||||
$this->response = new Response;
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
$this->container['logger']->debug('START_REQUEST='.$_SERVER['REQUEST_URI']);
|
$this->logger->debug('START_REQUEST='.$_SERVER['REQUEST_URI']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,14 +36,14 @@ abstract class Base extends \Kanboard\Core\Base
|
|||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
foreach ($this->container['db']->getLogMessages() as $message) {
|
foreach ($this->db->getLogMessages() as $message) {
|
||||||
$this->container['logger']->debug($message);
|
$this->logger->debug($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries));
|
$this->logger->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries));
|
||||||
$this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
|
$this->logger->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
|
||||||
$this->container['logger']->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage()));
|
$this->logger->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage()));
|
||||||
$this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']);
|
$this->logger->debug('END_REQUEST='.$_SERVER['REQUEST_URI']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +180,7 @@ abstract class Base extends \Kanboard\Core\Base
|
|||||||
*/
|
*/
|
||||||
protected function checkCSRFParam()
|
protected function checkCSRFParam()
|
||||||
{
|
{
|
||||||
if (! Security::validateCSRFToken($this->request->getStringParam('csrf_token'))) {
|
if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
|
||||||
$this->forbidden();
|
$this->forbidden();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Kanboard\Core;
|
|||||||
* @package core
|
* @package core
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Request
|
class Request extends Base
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get URL string parameter
|
* Get URL string parameter
|
||||||
@@ -57,7 +57,8 @@ class Request
|
|||||||
*/
|
*/
|
||||||
public function getValues()
|
public function getValues()
|
||||||
{
|
{
|
||||||
if (! empty($_POST) && Security::validateCSRFFormToken($_POST)) {
|
if (! empty($_POST) && ! empty($_POST['csrf_token']) && $this->token->validateCSRFToken($_POST['csrf_token'])) {
|
||||||
|
unset($_POST['csrf_token']);
|
||||||
return $_POST;
|
return $_POST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Kanboard\Core;
|
|||||||
* @package core
|
* @package core
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Response
|
class Response extends Base
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Send no cache headers
|
* Send no cache headers
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Kanboard\Core;
|
namespace Kanboard\Core\Security;
|
||||||
|
|
||||||
|
use Kanboard\Core\Base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security class
|
* Token Handler
|
||||||
*
|
*
|
||||||
* @package core
|
* @package security
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Security
|
class Token extends Base
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
|
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
|
||||||
@@ -17,7 +19,7 @@ class Security
|
|||||||
* @access public
|
* @access public
|
||||||
* @return string Random token
|
* @return string Random token
|
||||||
*/
|
*/
|
||||||
public static function generateToken()
|
public static function getToken()
|
||||||
{
|
{
|
||||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||||
return bin2hex(\openssl_random_pseudo_bytes(30));
|
return bin2hex(\openssl_random_pseudo_bytes(30));
|
||||||
@@ -31,18 +33,16 @@ class Security
|
|||||||
/**
|
/**
|
||||||
* Generate and store a CSRF token in the current session
|
* Generate and store a CSRF token in the current session
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @access public
|
* @access public
|
||||||
* @return string Random token
|
* @return string Random token
|
||||||
*/
|
*/
|
||||||
public static function getCSRFToken()
|
public function getCSRFToken()
|
||||||
{
|
{
|
||||||
$nonce = self::generateToken();
|
if (! isset($_SESSION['csrf_tokens'])) {
|
||||||
|
|
||||||
if (empty($_SESSION['csrf_tokens'])) {
|
|
||||||
$_SESSION['csrf_tokens'] = array();
|
$_SESSION['csrf_tokens'] = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$nonce = self::getToken();
|
||||||
$_SESSION['csrf_tokens'][$nonce] = true;
|
$_SESSION['csrf_tokens'][$nonce] = true;
|
||||||
|
|
||||||
return $nonce;
|
return $nonce;
|
||||||
@@ -51,12 +51,11 @@ class Security
|
|||||||
/**
|
/**
|
||||||
* Check if the token exists for the current session (a token can be used only one time)
|
* Check if the token exists for the current session (a token can be used only one time)
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @access public
|
* @access public
|
||||||
* @param string $token CSRF token
|
* @param string $token CSRF token
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function validateCSRFToken($token)
|
public function validateCSRFToken($token)
|
||||||
{
|
{
|
||||||
if (isset($_SESSION['csrf_tokens'][$token])) {
|
if (isset($_SESSION['csrf_tokens'][$token])) {
|
||||||
unset($_SESSION['csrf_tokens'][$token]);
|
unset($_SESSION['csrf_tokens'][$token]);
|
||||||
@@ -65,22 +64,4 @@ class Security
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the token used in a form is correct and then remove the value
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
* @access public
|
|
||||||
* @param array $values Form values
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function validateCSRFFormToken(array &$values)
|
|
||||||
{
|
|
||||||
if (! empty($values['csrf_token']) && self::validateCSRFToken($values['csrf_token'])) {
|
|
||||||
unset($values['csrf_token']);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Kanboard\Helper;
|
namespace Kanboard\Helper;
|
||||||
|
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form helpers
|
* Form helpers
|
||||||
@@ -10,7 +10,7 @@ use Kanboard\Core\Security;
|
|||||||
* @package helper
|
* @package helper
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Form extends \Kanboard\Core\Base
|
class Form extends Base
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Hidden CSRF token field
|
* Hidden CSRF token field
|
||||||
@@ -20,7 +20,7 @@ class Form extends \Kanboard\Core\Base
|
|||||||
*/
|
*/
|
||||||
public function csrf()
|
public function csrf()
|
||||||
{
|
{
|
||||||
return '<input type="hidden" name="csrf_token" value="'.Security::getCSRFToken().'"/>';
|
return '<input type="hidden" name="csrf_token" value="'.$this->token->getCSRFToken().'"/>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Kanboard\Helper;
|
namespace Kanboard\Helper;
|
||||||
|
|
||||||
use Kanboard\Core\Request;
|
use Kanboard\Core\Request;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Base;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Url helpers
|
* Url helpers
|
||||||
@@ -11,7 +11,7 @@ use Kanboard\Core\Security;
|
|||||||
* @package helper
|
* @package helper
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
*/
|
*/
|
||||||
class Url extends \Kanboard\Core\Base
|
class Url extends Base
|
||||||
{
|
{
|
||||||
private $base = '';
|
private $base = '';
|
||||||
private $directory = '';
|
private $directory = '';
|
||||||
@@ -158,7 +158,7 @@ class Url extends \Kanboard\Core\Base
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($csrf) {
|
if ($csrf) {
|
||||||
$qs['csrf_token'] = Security::getCSRFToken();
|
$qs['csrf_token'] = $this->token->getCSRFToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! empty($qs)) {
|
if (! empty($qs)) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Kanboard\Model;
|
namespace Kanboard\Model;
|
||||||
|
|
||||||
use Kanboard\Core\Translator;
|
use Kanboard\Core\Translator;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
use Kanboard\Core\Session;
|
use Kanboard\Core\Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,7 +265,7 @@ class Config extends Setting
|
|||||||
*/
|
*/
|
||||||
public function regenerateToken($option)
|
public function regenerateToken($option)
|
||||||
{
|
{
|
||||||
$this->save(array($option => Security::generateToken()));
|
$this->save(array($option => Token::getToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Kanboard\Model;
|
|||||||
|
|
||||||
use SimpleValidator\Validator;
|
use SimpleValidator\Validator;
|
||||||
use SimpleValidator\Validators;
|
use SimpleValidator\Validators;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project model
|
* Project model
|
||||||
@@ -491,7 +491,7 @@ class Project extends Base
|
|||||||
$this->db
|
$this->db
|
||||||
->table(self::TABLE)
|
->table(self::TABLE)
|
||||||
->eq('id', $project_id)
|
->eq('id', $project_id)
|
||||||
->save(array('is_public' => 1, 'token' => Security::generateToken()));
|
->save(array('is_public' => 1, 'token' => Token::getToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use PicoDb\Database;
|
|||||||
use SimpleValidator\Validator;
|
use SimpleValidator\Validator;
|
||||||
use SimpleValidator\Validators;
|
use SimpleValidator\Validators;
|
||||||
use Kanboard\Core\Session;
|
use Kanboard\Core\Session;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User model
|
* User model
|
||||||
@@ -383,7 +383,7 @@ class User extends Base
|
|||||||
return $this->db
|
return $this->db
|
||||||
->table(self::TABLE)
|
->table(self::TABLE)
|
||||||
->eq('id', $user_id)
|
->eq('id', $user_id)
|
||||||
->save(array('token' => Security::generateToken()));
|
->save(array('token' => Token::getToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Schema;
|
namespace Schema;
|
||||||
|
|
||||||
use PDO;
|
use PDO;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
const VERSION = 93;
|
const VERSION = 93;
|
||||||
|
|
||||||
@@ -869,7 +869,7 @@ function version_20(PDO $pdo)
|
|||||||
function version_19(PDO $pdo)
|
function version_19(PDO $pdo)
|
||||||
{
|
{
|
||||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT ''");
|
$pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT ''");
|
||||||
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
|
$pdo->exec("UPDATE config SET api_token='".Token::getToken()."'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function version_18(PDO $pdo)
|
function version_18(PDO $pdo)
|
||||||
@@ -1091,6 +1091,6 @@ function version_1(PDO $pdo)
|
|||||||
$pdo->exec("
|
$pdo->exec("
|
||||||
INSERT INTO config
|
INSERT INTO config
|
||||||
(webhooks_token)
|
(webhooks_token)
|
||||||
VALUES ('".Security::generateToken()."')
|
VALUES ('".Token::getToken()."')
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace Schema;
|
namespace Schema;
|
||||||
|
|
||||||
use PDO;
|
use PDO;
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
const VERSION = 73;
|
const VERSION = 73;
|
||||||
|
|
||||||
@@ -994,6 +994,6 @@ function version_1(PDO $pdo)
|
|||||||
$pdo->exec("
|
$pdo->exec("
|
||||||
INSERT INTO config
|
INSERT INTO config
|
||||||
(webhooks_token, api_token)
|
(webhooks_token, api_token)
|
||||||
VALUES ('".Security::generateToken()."', '".Security::generateToken()."')
|
VALUES ('".Token::getToken()."', '".Token::getToken()."')
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Schema;
|
namespace Schema;
|
||||||
|
|
||||||
use Kanboard\Core\Security;
|
use Kanboard\Core\Security\Token;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
const VERSION = 88;
|
const VERSION = 88;
|
||||||
@@ -799,7 +799,7 @@ function version_20(PDO $pdo)
|
|||||||
function version_19(PDO $pdo)
|
function version_19(PDO $pdo)
|
||||||
{
|
{
|
||||||
$pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT ''");
|
$pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT ''");
|
||||||
$pdo->exec("UPDATE config SET api_token='".Security::generateToken()."'");
|
$pdo->exec("UPDATE config SET api_token='".Token::getToken()."'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function version_18(PDO $pdo)
|
function version_18(PDO $pdo)
|
||||||
@@ -1068,6 +1068,6 @@ function version_1(PDO $pdo)
|
|||||||
$pdo->exec("
|
$pdo->exec("
|
||||||
INSERT INTO config
|
INSERT INTO config
|
||||||
(webhooks_token)
|
(webhooks_token)
|
||||||
VALUES ('".Security::generateToken()."')
|
VALUES ('".Token::getToken()."')
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class ClassProvider implements ServiceProviderInterface
|
|||||||
'HttpClient',
|
'HttpClient',
|
||||||
'Lexer',
|
'Lexer',
|
||||||
'Request',
|
'Request',
|
||||||
|
'Response',
|
||||||
'Router',
|
'Router',
|
||||||
'Session',
|
'Session',
|
||||||
'Template',
|
'Template',
|
||||||
@@ -94,6 +95,9 @@ class ClassProvider implements ServiceProviderInterface
|
|||||||
'Core\Plugin' => array(
|
'Core\Plugin' => array(
|
||||||
'Hook',
|
'Hook',
|
||||||
),
|
),
|
||||||
|
'Core\Security' => array(
|
||||||
|
'Token',
|
||||||
|
),
|
||||||
'Integration' => array(
|
'Integration' => array(
|
||||||
'BitbucketWebhook',
|
'BitbucketWebhook',
|
||||||
'GithubWebhook',
|
'GithubWebhook',
|
||||||
|
|||||||
29
tests/units/Core/Security/TokenTest.php
Normal file
29
tests/units/Core/Security/TokenTest.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__.'/../../Base.php';
|
||||||
|
|
||||||
|
use Kanboard\Core\Security\Token;
|
||||||
|
|
||||||
|
class TokenTest extends Base
|
||||||
|
{
|
||||||
|
public function testGenerateToken()
|
||||||
|
{
|
||||||
|
$t1 = Token::getToken();
|
||||||
|
$t2 = Token::getToken();
|
||||||
|
|
||||||
|
$this->assertNotEmpty($t1);
|
||||||
|
$this->assertNotEmpty($t2);
|
||||||
|
|
||||||
|
$this->assertNotEquals($t1, $t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCSRFTokens()
|
||||||
|
{
|
||||||
|
$token = new Token($this->container);
|
||||||
|
$t1 = $token->getCSRFToken();
|
||||||
|
|
||||||
|
$this->assertNotEmpty($t1);
|
||||||
|
$this->assertTrue($token->validateCSRFToken($t1));
|
||||||
|
$this->assertFalse($token->validateCSRFToken($t1));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user