From 1f3dbb87f42cf5bfbd5a59de4977ddeb481202f2 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Mon, 9 Sep 2019 09:52:10 +0000 Subject: [PATCH] csrf : allow multiple browser window by storing multiple CSRF tokens git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@5458 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- csrf.php | 74 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/csrf.php b/csrf.php index 56d0876..89cb653 100644 --- a/csrf.php +++ b/csrf.php @@ -11,11 +11,14 @@ */ class csrf { - /** Allow to disable the csrf protection */ - public $csrf=TRUE; - /** This hidden field name in HTML */ + /** Allow to disable the csrf protection + */ + public $csrf = TRUE; + /** This hidden field name in HTML + */ public $field = "CSRF_TOKEN"; - /** The created token */ + /** The created token + */ private $csrfToken = ""; /** Timeout of the CSRF token : 3600s by default (maximum time allowed to * enter information in form and submit) */ @@ -39,6 +42,32 @@ class csrf } // }}} + /** Get / Set the status of the CSRF protection + */ + public function csrfState ($val = null) + // {{{ + { + if ($val === null) + return $this->csrf; + $this->csrf = !! $val; + $GLOBALS["domframework"]["csrf"] = $this; + return $this; + } + // }}} + + /** Get / Set the name of the field in HTML + */ + public function field ($val = null) + // {{{ + { + if ($val === null) + return $this->field; + $this->field = $val; + $GLOBALS["domframework"]["csrf"] = $this; + return $this; + } + // }}} + /** This function return the token */ public function createToken () @@ -50,8 +79,8 @@ class csrf $cl = strlen($c)-1, $i = 0; $i < $l; $s .= $c[mt_rand(0, $cl)], ++$i); $this->csrfToken = $s; - $_SESSION["domframework"]["csrf"]["csrf"] = $this->csrfToken; - $_SESSION["domframework"]["csrf"]["csrfStart"] = microtime (TRUE); + $_SESSION["domframework"]["csrf"][$this->csrfToken] = microtime (TRUE); + $GLOBALS["domframework"]["csrf"] = $this; return $this->csrfToken; } // }}} @@ -65,21 +94,36 @@ class csrf { if ($this->csrf === FALSE ) return TRUE; - if (! isset ($_SESSION["domframework"]["csrf"]["csrf"])) + // Migrate from unique format to multiple CSRF tokens format + // The new format is : array (token => the last used time) + if (isset ($_SESSION["domframework"]["csrf"]["csrfStart"])) { - throw new \Exception (dgettext ("domframework", - "No previous CSRF token : abort"), 406); + $_SESSION["domframework"]["csrf"] = array ( + $_SESSION["domframework"]["csrf"]["csrf"] => + $_SESSION["domframework"]["csrf"]["csrfStart"] + ); } - if ($_SESSION["domframework"]["csrf"]["csrf"] !== $tokenFromUser) + if (! isset ($_SESSION["domframework"]["csrf"])) { throw new \Exception (dgettext ("domframework", - "Invalid CSRF token provided"), 406); + "No previous CSRF token : abort"), 406); } - if (($_SESSION["domframework"]["csrf"]["csrfStart"] + $this->csrfTimeout) < - microtime (TRUE)) + if (! key_exists ($tokenFromUser, $_SESSION["domframework"]["csrf"])) { throw new \Exception (dgettext ("domframework", - "Obsolete CSRF token provided"), 406); + "Invalid CSRF token provided"), 406); + } + if (($_SESSION["domframework"]["csrf"][$tokenFromUser] + $this->csrfTimeout) + < microtime (TRUE)) + { + throw new \Exception (dgettext ("domframework", + "Obsolete CSRF token provided"), 406); + } + // Clean expired tokens (2 times the $this->csrfTimeout) + foreach ($_SESSION["domframework"]["csrf"] as $token => $timeout) + { + if ($timeout + 2 * $this->csrfTimeout < time ()) + unset ($_SESSION["domframework"]["csrf"][$token]); } return TRUE; } @@ -116,7 +160,7 @@ class csrf // {{{ { $this->checkToken ($tokenFromUser); - $_SESSION["domframework"]["csrf"]["csrfStart"] = microtime (TRUE); + $_SESSION["domframework"]["csrf"][$tokenFromUser] = microtime (TRUE); return true; } // }}}