diff --git a/composer.json b/composer.json index 43718c9a8..93f9ce4a3 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,7 @@ "monolog/monolog": "^1.22", "mpdf/mpdf": "^7.1", "mpociot/vat-calculator": "^2.3", + "owasp/csrf-protector-php": "^1.0", "phpmailer/phpmailer": "^6.0", "respect/validation": "^1.1", "servo/fluidxml": "^1.21", diff --git a/config/csrf_config.php b/config/csrf_config.php new file mode 100644 index 000000000..3075b2fa3 --- /dev/null +++ b/config/csrf_config.php @@ -0,0 +1,19 @@ + DOCROOT.'/logs', + 'failedAuthAction' => [ + 'GET' => 0, + 'POST' => 0, + ], + 'jsUrl' => ROOTDIR.'/assets/dist/js/csrf/csrfprotector.js', + 'tokenLength' => 10, + 'cookieConfig' => [ + 'path' => ROOTDIR, + 'secure' => isHTTPS(true), + ], + 'verifyGetFor' => [], +]; diff --git a/core.php b/core.php index 0c812204a..3496dd35e 100644 --- a/core.php +++ b/core.php @@ -132,7 +132,7 @@ if (!API::isAPIRequest()) { ini_set('session.use_trans_sid', '0'); ini_set('session.use_only_cookies', '1'); - session_set_cookie_params(0, $rootdir); + session_set_cookie_params(0, $rootdir, null, isHTTPS(true)); session_start(); } @@ -159,11 +159,8 @@ if (!API::isAPIRequest()) { // Impostazioni di Content-Type e Charset Header header('Content-Type: text/html; charset=UTF-8'); - /* // Controllo CSRF - if(!CSRF::getInstance()->validate()){ - die(tr('Constrollo CSRF fallito!')); - }*/ + csrfProtector::init(); // Aggiunta del wrapper personalizzato per la generazione degli input if (!empty($HTMLWrapper)) { diff --git a/gulpfile.js b/gulpfile.js index 433bf4e3c..60887259a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -206,6 +206,14 @@ gulp.task('chartjs', function () { .pipe(gulp.dest(config.production + '/' + config.paths.js + '/chartjs')); }); +gulp.task('csrf', function () { + gulp.src([ + './vendor/owasp/csrf-protector-php/js/csrfprotector.js', + ]) + .pipe(flatten()) + .pipe(gulp.dest(config.production + '/' + config.paths.js + '/csrf')); +}); + gulp.task('pdfjs', function () { gulp.src([ config.main.bowerDirectory + '/pdf/web/**/*', @@ -364,7 +372,7 @@ gulp.task('other', ['clean'], function () { gulp.start('chartjs'); gulp.start('php-debugbar'); - + gulp.start('csrf'); }); gulp.task('default', ['clean', 'bower']); diff --git a/src/CSRF.php b/src/CSRF.php deleted file mode 100644 index bac5375c5..000000000 --- a/src/CSRF.php +++ /dev/null @@ -1,305 +0,0 @@ -prefix = rtrim($prefix, '_'); - - $this->storage = &$storage; - $this->validateStorage(); - - $this->storageLimit = $storageLimit; - } - - /** - * @return string - */ - public function getToken() - { - if (!$this->keyPair && (!$this->isPersistent() || !$this->loadLastToken())) { - $this->generateToken(); - } - - return $this->keyPair; - } - - /** - * Generates a new CSRF token. - * - * @return array - */ - protected function generateToken() - { - // Generate new CSRF token - $name = uniqid($this->prefix); - $value = $this->createToken(); - $this->saveToStorage($name, $value); - - $this->keyPair = [ - $this->prefix.'_name' => $name, - $this->prefix.'_value' => $value, - ]; - - return $this->keyPair; - } - - public function validate() - { - $result = true; - - $this->validateStorage(); - - if (in_array($_SERVER['REQUEST_METHOD'], ['POST'])) { - $name = isset($_POST[$this->prefix.'_name']) ? $_POST[$this->prefix.'_name'] : false; - $value = isset($_POST[$this->prefix.'_value']) ? $_POST[$this->prefix.'_value'] : false; - - if (!$name || !$value || !$this->validateToken($name, $value)) { - // Need to regenerate a new token, as the validateToken removed the current one. - $this->generateToken(); - - $result = false; - } - } - - // Enforce the storage limit - $this->enforceStorageLimit(); - - return $result; - } - - /** - * @param $prefix - * @param $storage - * - * @return mixed - */ - protected function validateStorage() - { - if (is_array($this->storage)) { - return $this->storage; - } - - if ($this->storage instanceof ArrayAccess) { - return $this->storage; - } - - if (!array_key_exists($this->prefix, $_SESSION)) { - $_SESSION[$this->prefix] = []; - } - - $this->storage = &$_SESSION[$this->prefix]; - - return $this->storage; - } - - /** - * Validate CSRF token from current request against token value stored in $_SESSION. - * - * @param string $name CSRF name - * @param string $value CSRF token value - * - * @return bool - */ - protected function validateToken($name, $value) - { - $token = $this->getFromStorage($name); - if (function_exists('hash_equals')) { - $result = ($token !== false && hash_equals($token, $value)); - } else { - $result = ($token !== false && $token === $value); - } - - // If we're not in persistent token mode, delete the token. - if (!$this->isPersistent() || !$result) { - $this->removeFromStorage($name); - } - - return $result; - } - - /** - * Create CSRF token value. - * - * @return string - */ - protected function createToken() - { - return bin2hex(random_bytes($this->strength)); - } - - /** - * Save token to storage. - * - * @param string $name CSRF token name - * @param string $value CSRF token value - */ - protected function saveToStorage($name, $value) - { - $this->storage[$name] = $value; - } - - /** - * Get token from storage. - * - * @param string $name CSRF token name - * - * @return string|bool CSRF token value or `false` if not present - */ - protected function getFromStorage($name) - { - return isset($this->storage[$name]) ? $this->storage[$name] : false; - } - - /** - * Get the most recent key pair from storage. - * - * @return string[]|null Array containing name and value if found, null otherwise - */ - protected function loadLastToken() - { - // Use count, since empty ArrayAccess objects can still return false for `empty` - if (count($this->storage) < 1) { - return null; - } - - foreach ($this->storage as $name => $value) { - continue; - } - - $keyPair = [ - $this->prefix.'_name' => $name, - $this->prefix.'_value' => $value, - ]; - - if ($keyPair) { - $this->keyPair = $keyPair; - - return true; - } - - return false; - } - - /** - * Remove token from storage. - * - * @param string $name CSRF token name - */ - protected function removeFromStorage($name) - { - $this->storage[$name] = ' '; - unset($this->storage[$name]); - } - - /** - * Remove the oldest tokens from the storage array so that there - * are never more than storageLimit tokens in the array. - * - * This is required as a token is generated every request and so - * most will never be used. - */ - protected function enforceStorageLimit() - { - if ($this->storageLimit < 1) { - return; - } - - // $storage must be an array or implement Countable and Traversable - if (!is_array($this->storage) - && !($this->storage instanceof Countable && $this->storage instanceof Traversable) - ) { - return; - } - - if (is_array($this->storage)) { - while (count($this->storage) > $this->storageLimit) { - array_shift($this->storage); - } - } else { - // array_shift() doesn't work for ArrayAccess, so we need an iterator in order to use rewind() - // and key(), so that we can then unset - $iterator = $this->storage; - if ($this->storage instanceof \IteratorAggregate) { - $iterator = $this->storage->getIterator(); - } - while (count($this->storage) > $this->storageLimit) { - $iterator->rewind(); - unset($this->storage[$iterator->key()]); - } - } - } - - /** - * Setter for storageLimit. - * - * @param int $storageLimit Value to set - * - * @return $this - */ - public function setStorageLimit($storageLimit) - { - $this->storageLimit = (int) $storageLimit; - } - - /** - * Getter for persistentTokenMode. - * - * @return bool - */ - public function isPersistent() - { - return $this->storageLimit < 0; - } -}