From 146a36a79a1e1ea71842acad731b9a5896e9cf72 Mon Sep 17 00:00:00 2001 From: Sebastian Rapetti Date: Sun, 20 Aug 2017 11:04:39 +0200 Subject: [PATCH 1/4] getTimedToken() added --- CHANGELOG.md | 13 +++++- src/CsrfGuard.php | 87 +++++++++++++++++++++++++++++++++++------ tests/CsrfGuardTest.php | 37 +++++++++++++++--- 3 files changed, 119 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f679653..db42b44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [v1.0.1](https://github.com/linna/csrf-guard/compare/v1.0.0...v1.0.1) - 2017-XX-XX +## [v1.1.0](https://github.com/linna/csrf-guard/compare/v1.0.0...v1.1.0) - 2017-XX-XX + +### Added +* `getTimedToken()` method for expiring tokens + +### Changed +* `validate()` naw can validate for timed tokens +* Tests updated +* Internal methods refactor + +### Deprecated +* `getHiddenInput()` method ### Fixed * `private $session;` docblock diff --git a/src/CsrfGuard.php b/src/CsrfGuard.php index 1e82b6f..0d11bff 100644 --- a/src/CsrfGuard.php +++ b/src/CsrfGuard.php @@ -77,30 +77,67 @@ private function dequeue(array &$array) */ public function getToken() : array { - $tokenName = 'csrf_'.bin2hex(random_bytes(8)); - $token = bin2hex(random_bytes($this->tokenStrength)); + $token = $this->generateToken(); - $this->session['CSRF'][$tokenName] = $token; + $name = $token['name']; + + $this->session['CSRF'][$name] = $token; //storage cleaning! //warning!! if you get in a page more token of maximun storage, //will there a leak of token, the firsts generated //in future I think throw and exception. $this->dequeue($this->session['CSRF']); - - return ['name' => $tokenName, 'token' => $token]; + + return $token; } + /** + * Return timed csrf token as array. + * + * @param int $ttl Time to live for the token. + * + * @return array + */ + public function getTimedToken(int $ttl) : array + { + $token = $this->generateToken(); + $token['time'] = time() + $ttl; + + $name = $token['name']; + + $this->session['CSRF'][$name] = $token; + + $this->dequeue($this->session['CSRF']); + + return $token; + } + + /** + * Generate a random token. + * + * @return array + */ + private function generateToken() : array + { + $name = 'csrf_'.bin2hex(random_bytes(8)); + $value = bin2hex(random_bytes($this->tokenStrength)); + + return ['name' => $name, 'value' => $value]; + } + /** * Return csrf token as hidden input form. * * @return string + * + * @deprecated since version 1.1.0 */ public function getHiddenInput() : string { $token = $this->getToken(); - return ''; + return ''; } /** @@ -113,14 +150,40 @@ public function getHiddenInput() : string */ public function validate(array $requestData) : bool { - $arrayToken = $this->session['CSRF']; + //apply matchToken method elements of passed data, + //using this instead of forach for code shortness. + $array = array_filter($requestData, array($this, 'matchToken'), ARRAY_FILTER_USE_BOTH); + + return (bool) count($array); + } + + /** + * Tests for valid token. + * + * @param string $value + * @param string $key + * + * @return bool + */ + private function matchToken(string $value, string $key) : bool + { + $tokens = $this->session['CSRF']; - foreach ($requestData as $key => $value) { - if (isset($arrayToken[$key]) && hash_equals($arrayToken[$key], $value)) { - return true; - } + //check if token exist + if (!isset($tokens[$key])) { + return false; } - return false; + //check if token is valid + if (!hash_equals($tokens[$key]['value'], $value)) { + return false; + } + + //check if token is expired if timed + if (isset($tokens[$key]['time']) && $tokens[$key]['time'] < time()) { + return false; + } + + return true; } } diff --git a/tests/CsrfGuardTest.php b/tests/CsrfGuardTest.php index a132954..8e8105e 100644 --- a/tests/CsrfGuardTest.php +++ b/tests/CsrfGuardTest.php @@ -113,15 +113,42 @@ public function testDequeue(int $sizeLimit) public function testGetToken() { session_start(); - + $csrf = new CsrfGuard(32, 16); - + $token = $csrf->getToken(); + + $key = key($_SESSION['CSRF']); + $value = $_SESSION['CSRF'][$key]['value']; + + $this->assertEquals($key, $token['name']); + $this->assertEquals($value, $token['value']); + + session_destroy(); + } + + /** + * Test get timed token. + * + * @runInSeparateProcess + */ + public function testGetTimedToken() + { + session_start(); + + $csrf = new CsrfGuard(32, 16); + + $token = $csrf->getTimedToken(5); + $tokenTime = time() + 5; $key = key($_SESSION['CSRF']); + $value = $_SESSION['CSRF'][$key]['value']; + $time = $_SESSION['CSRF'][$key]['time']; $this->assertEquals($key, $token['name']); - $this->assertEquals($_SESSION['CSRF'][$key], $token['token']); + $this->assertEquals($value, $token['value']); + $this->assertEquals($time, $token['time']); + $this->assertEquals($tokenTime, $token['time']); session_destroy(); } @@ -140,7 +167,7 @@ public function testGetHiddenInput() $input = $csrf->getHiddenInput(); $key = key($_SESSION['CSRF']); - $token = $_SESSION['CSRF'][$key]; + $token = $_SESSION['CSRF'][$key]['value']; $this->assertEquals('', $input); @@ -160,7 +187,7 @@ public function testValidate() $csrf->getToken(); $key = key($_SESSION['CSRF']); - $token = $_SESSION['CSRF'][$key]; + $token = $_SESSION['CSRF'][$key]['value']; $this->assertEquals(true, $csrf->validate([$key => $token])); $this->assertEquals(false, $csrf->validate(['foo' => $token])); From 0c3c9862b39c2cb4f9a4ead5cc47be40472245d1 Mon Sep 17 00:00:00 2001 From: Sebastian Rapetti Date: Sun, 20 Aug 2017 11:10:05 +0200 Subject: [PATCH 2/4] php 7.2 added --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index b88ac90..0230d90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,13 @@ language: php php: - 7.0 - 7.1 +- 7.2 - nightly matrix: allow_failures: - php: nightly + - php: 7.2 before_script: - composer install From ed3173e59a7e62fd2d4692cd8a4774a3f659e4a2 Mon Sep 17 00:00:00 2001 From: Sebastian Rapetti Date: Sun, 20 Aug 2017 11:10:12 +0200 Subject: [PATCH 3/4] style fixed --- src/CsrfGuard.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/CsrfGuard.php b/src/CsrfGuard.php index 0d11bff..784b65f 100644 --- a/src/CsrfGuard.php +++ b/src/CsrfGuard.php @@ -96,7 +96,7 @@ public function getToken() : array * Return timed csrf token as array. * * @param int $ttl Time to live for the token. - * + * * @return array */ public function getTimedToken(int $ttl) : array @@ -115,7 +115,7 @@ public function getTimedToken(int $ttl) : array /** * Generate a random token. - * + * * @return array */ private function generateToken() : array @@ -130,7 +130,7 @@ private function generateToken() : array * Return csrf token as hidden input form. * * @return string - * + * * @deprecated since version 1.1.0 */ public function getHiddenInput() : string @@ -162,7 +162,7 @@ public function validate(array $requestData) : bool * * @param string $value * @param string $key - * + * * @return bool */ private function matchToken(string $value, string $key) : bool @@ -177,13 +177,13 @@ private function matchToken(string $value, string $key) : bool //check if token is valid if (!hash_equals($tokens[$key]['value'], $value)) { return false; - } + } //check if token is expired if timed if (isset($tokens[$key]['time']) && $tokens[$key]['time'] < time()) { return false; } - return true; + return true; } } From 4aeddd43e4f30a6960fc64d76dcfe388e4fe685d Mon Sep 17 00:00:00 2001 From: Sebastian Rapetti Date: Sun, 20 Aug 2017 11:16:13 +0200 Subject: [PATCH 4/4] v1.1.0 release --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db42b44..7dcc63d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [v1.1.0](https://github.com/linna/csrf-guard/compare/v1.0.0...v1.1.0) - 2017-XX-XX +## [v1.1.0](https://github.com/linna/csrf-guard/compare/v1.0.0...v1.1.0) - 2017-08-20 ### Added * `getTimedToken()` method for expiring tokens