diff options
-rw-r--r-- | app/Auth/RememberMe.php | 8 | ||||
-rw-r--r-- | app/Controller/Base.php | 37 | ||||
-rw-r--r-- | app/Core/Request.php | 5 | ||||
-rw-r--r-- | app/Core/Response.php | 2 | ||||
-rw-r--r-- | app/Core/Security/Token.php (renamed from app/Core/Security.php) | 41 | ||||
-rw-r--r-- | app/Helper/Form.php | 6 | ||||
-rw-r--r-- | app/Helper/Url.php | 6 | ||||
-rw-r--r-- | app/Model/Config.php | 4 | ||||
-rw-r--r-- | app/Model/Project.php | 4 | ||||
-rw-r--r-- | app/Model/User.php | 4 | ||||
-rw-r--r-- | app/Schema/Mysql.php | 6 | ||||
-rw-r--r-- | app/Schema/Postgres.php | 4 | ||||
-rw-r--r-- | app/Schema/Sqlite.php | 6 | ||||
-rw-r--r-- | app/ServiceProvider/ClassProvider.php | 4 | ||||
-rw-r--r-- | tests/units/Core/Security/TokenTest.php | 29 |
15 files changed, 80 insertions, 86 deletions
diff --git a/app/Auth/RememberMe.php b/app/Auth/RememberMe.php index 0290e36c..24f30a2c 100644 --- a/app/Auth/RememberMe.php +++ b/app/Auth/RememberMe.php @@ -5,7 +5,7 @@ namespace Kanboard\Auth; use Kanboard\Core\Base; use Kanboard\Core\Request; use Kanboard\Event\AuthEvent; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; /** * RememberMe model @@ -165,8 +165,8 @@ class RememberMe extends Base */ public function create($user_id, $ip, $user_agent) { - $token = hash('sha256', $user_id.$user_agent.$ip.Security::generateToken()); - $sequence = Security::generateToken(); + $token = hash('sha256', $user_id.$user_agent.$ip.Token::getToken()); + $sequence = Token::getToken(); $expiration = time() + self::EXPIRATION; $this->cleanup($user_id); @@ -216,7 +216,7 @@ class RememberMe extends Base */ public function update($token) { - $new_sequence = Security::generateToken(); + $new_sequence = Token::getToken(); $this->db ->table(self::TABLE) diff --git a/app/Controller/Base.php b/app/Controller/Base.php index a955b12c..829e0ad2 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -3,9 +3,6 @@ namespace Kanboard\Controller; use Pimple\Container; -use Kanboard\Core\Security; -use Kanboard\Core\Request; -use Kanboard\Core\Response; use Symfony\Component\EventDispatcher\Event; /** @@ -17,22 +14,6 @@ use Symfony\Component\EventDispatcher\Event; 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 * * @access public @@ -41,11 +22,9 @@ abstract class Base extends \Kanboard\Core\Base public function __construct(Container $container) { $this->container = $container; - $this->request = new Request; - $this->response = new Response; 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() { if (DEBUG) { - foreach ($this->container['db']->getLogMessages() as $message) { - $this->container['logger']->debug($message); + foreach ($this->db->getLogMessages() as $message) { + $this->logger->debug($message); } - $this->container['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->container['logger']->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage())); - $this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']); + $this->logger->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries)); + $this->logger->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT'])); + $this->logger->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage())); + $this->logger->debug('END_REQUEST='.$_SERVER['REQUEST_URI']); } } @@ -201,7 +180,7 @@ abstract class Base extends \Kanboard\Core\Base */ protected function checkCSRFParam() { - if (! Security::validateCSRFToken($this->request->getStringParam('csrf_token'))) { + if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) { $this->forbidden(); } } diff --git a/app/Core/Request.php b/app/Core/Request.php index 5eda2d02..0398760e 100644 --- a/app/Core/Request.php +++ b/app/Core/Request.php @@ -8,7 +8,7 @@ namespace Kanboard\Core; * @package core * @author Frederic Guillot */ -class Request +class Request extends Base { /** * Get URL string parameter @@ -57,7 +57,8 @@ class Request */ 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; } diff --git a/app/Core/Response.php b/app/Core/Response.php index 528a6302..6788473a 100644 --- a/app/Core/Response.php +++ b/app/Core/Response.php @@ -8,7 +8,7 @@ namespace Kanboard\Core; * @package core * @author Frederic Guillot */ -class Response +class Response extends Base { /** * Send no cache headers diff --git a/app/Core/Security.php b/app/Core/Security/Token.php index 54207ee1..7aca08af 100644 --- a/app/Core/Security.php +++ b/app/Core/Security/Token.php @@ -1,14 +1,16 @@ <?php -namespace Kanboard\Core; +namespace Kanboard\Core\Security; + +use Kanboard\Core\Base; /** - * Security class + * Token Handler * - * @package core + * @package security * @author Frederic Guillot */ -class Security +class Token extends Base { /** * Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid() @@ -17,7 +19,7 @@ class Security * @access public * @return string Random token */ - public static function generateToken() + public static function getToken() { if (function_exists('openssl_random_pseudo_bytes')) { return bin2hex(\openssl_random_pseudo_bytes(30)); @@ -31,18 +33,16 @@ class Security /** * Generate and store a CSRF token in the current session * - * @static * @access public * @return string Random token */ - public static function getCSRFToken() + public function getCSRFToken() { - $nonce = self::generateToken(); - - if (empty($_SESSION['csrf_tokens'])) { + if (! isset($_SESSION['csrf_tokens'])) { $_SESSION['csrf_tokens'] = array(); } + $nonce = self::getToken(); $_SESSION['csrf_tokens'][$nonce] = true; 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) * - * @static * @access public * @param string $token CSRF token * @return bool */ - public static function validateCSRFToken($token) + public function validateCSRFToken($token) { if (isset($_SESSION['csrf_tokens'][$token])) { unset($_SESSION['csrf_tokens'][$token]); @@ -65,22 +64,4 @@ class Security 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; - } } diff --git a/app/Helper/Form.php b/app/Helper/Form.php index 5f19f2a8..bfd75ee3 100644 --- a/app/Helper/Form.php +++ b/app/Helper/Form.php @@ -2,7 +2,7 @@ namespace Kanboard\Helper; -use Kanboard\Core\Security; +use Kanboard\Core\Base; /** * Form helpers @@ -10,7 +10,7 @@ use Kanboard\Core\Security; * @package helper * @author Frederic Guillot */ -class Form extends \Kanboard\Core\Base +class Form extends Base { /** * Hidden CSRF token field @@ -20,7 +20,7 @@ class Form extends \Kanboard\Core\Base */ public function csrf() { - return '<input type="hidden" name="csrf_token" value="'.Security::getCSRFToken().'"/>'; + return '<input type="hidden" name="csrf_token" value="'.$this->token->getCSRFToken().'"/>'; } /** diff --git a/app/Helper/Url.php b/app/Helper/Url.php index f120252d..e47256ba 100644 --- a/app/Helper/Url.php +++ b/app/Helper/Url.php @@ -3,7 +3,7 @@ namespace Kanboard\Helper; use Kanboard\Core\Request; -use Kanboard\Core\Security; +use Kanboard\Core\Base; /** * Url helpers @@ -11,7 +11,7 @@ use Kanboard\Core\Security; * @package helper * @author Frederic Guillot */ -class Url extends \Kanboard\Core\Base +class Url extends Base { private $base = ''; private $directory = ''; @@ -158,7 +158,7 @@ class Url extends \Kanboard\Core\Base } if ($csrf) { - $qs['csrf_token'] = Security::getCSRFToken(); + $qs['csrf_token'] = $this->token->getCSRFToken(); } if (! empty($qs)) { diff --git a/app/Model/Config.php b/app/Model/Config.php index cf634f80..3b90f58d 100644 --- a/app/Model/Config.php +++ b/app/Model/Config.php @@ -3,7 +3,7 @@ namespace Kanboard\Model; use Kanboard\Core\Translator; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; use Kanboard\Core\Session; /** @@ -265,7 +265,7 @@ class Config extends Setting */ public function regenerateToken($option) { - $this->save(array($option => Security::generateToken())); + $this->save(array($option => Token::getToken())); } /** diff --git a/app/Model/Project.php b/app/Model/Project.php index b767af26..9e30a9b8 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -4,7 +4,7 @@ namespace Kanboard\Model; use SimpleValidator\Validator; use SimpleValidator\Validators; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; /** * Project model @@ -491,7 +491,7 @@ class Project extends Base $this->db ->table(self::TABLE) ->eq('id', $project_id) - ->save(array('is_public' => 1, 'token' => Security::generateToken())); + ->save(array('is_public' => 1, 'token' => Token::getToken())); } /** diff --git a/app/Model/User.php b/app/Model/User.php index 6e7e94e0..dc00c0c5 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -6,7 +6,7 @@ use PicoDb\Database; use SimpleValidator\Validator; use SimpleValidator\Validators; use Kanboard\Core\Session; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; /** * User model @@ -383,7 +383,7 @@ class User extends Base return $this->db ->table(self::TABLE) ->eq('id', $user_id) - ->save(array('token' => Security::generateToken())); + ->save(array('token' => Token::getToken())); } /** diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index a021c1cc..54d58592 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -3,7 +3,7 @@ namespace Schema; use PDO; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; const VERSION = 93; @@ -869,7 +869,7 @@ function version_20(PDO $pdo) function version_19(PDO $pdo) { $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) @@ -1091,6 +1091,6 @@ function version_1(PDO $pdo) $pdo->exec(" INSERT INTO config (webhooks_token) - VALUES ('".Security::generateToken()."') + VALUES ('".Token::getToken()."') "); } diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index a3fb6d49..6f7efed0 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -3,7 +3,7 @@ namespace Schema; use PDO; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; const VERSION = 73; @@ -994,6 +994,6 @@ function version_1(PDO $pdo) $pdo->exec(" INSERT INTO config (webhooks_token, api_token) - VALUES ('".Security::generateToken()."', '".Security::generateToken()."') + VALUES ('".Token::getToken()."', '".Token::getToken()."') "); } diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index b9ab86f8..d27f11ec 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -2,7 +2,7 @@ namespace Schema; -use Kanboard\Core\Security; +use Kanboard\Core\Security\Token; use PDO; const VERSION = 88; @@ -799,7 +799,7 @@ function version_20(PDO $pdo) function version_19(PDO $pdo) { $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) @@ -1068,6 +1068,6 @@ function version_1(PDO $pdo) $pdo->exec(" INSERT INTO config (webhooks_token) - VALUES ('".Security::generateToken()."') + VALUES ('".Token::getToken()."') "); } diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index c103d639..c1a59f85 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -84,6 +84,7 @@ class ClassProvider implements ServiceProviderInterface 'HttpClient', 'Lexer', 'Request', + 'Response', 'Router', 'Session', 'Template', @@ -94,6 +95,9 @@ class ClassProvider implements ServiceProviderInterface 'Core\Plugin' => array( 'Hook', ), + 'Core\Security' => array( + 'Token', + ), 'Integration' => array( 'BitbucketWebhook', 'GithubWebhook', diff --git a/tests/units/Core/Security/TokenTest.php b/tests/units/Core/Security/TokenTest.php new file mode 100644 index 00000000..dbb7bd1a --- /dev/null +++ b/tests/units/Core/Security/TokenTest.php @@ -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)); + } +} |