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
This commit is contained in:
2019-09-09 09:52:10 +00:00
parent 2212237752
commit 1f3dbb87f4

View File

@@ -11,11 +11,14 @@
*/ */
class csrf class csrf
{ {
/** Allow to disable the csrf protection */ /** Allow to disable the csrf protection
*/
public $csrf = TRUE; public $csrf = TRUE;
/** This hidden field name in HTML */ /** This hidden field name in HTML
*/
public $field = "CSRF_TOKEN"; public $field = "CSRF_TOKEN";
/** The created token */ /** The created token
*/
private $csrfToken = ""; private $csrfToken = "";
/** Timeout of the CSRF token : 3600s by default (maximum time allowed to /** Timeout of the CSRF token : 3600s by default (maximum time allowed to
* enter information in form and submit) */ * 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 /** This function return the token
*/ */
public function createToken () public function createToken ()
@@ -50,8 +79,8 @@ class csrf
$cl = strlen($c)-1, $i = 0; $i < $l; $s .= $c[mt_rand(0, $cl)], $cl = strlen($c)-1, $i = 0; $i < $l; $s .= $c[mt_rand(0, $cl)],
++$i); ++$i);
$this->csrfToken = $s; $this->csrfToken = $s;
$_SESSION["domframework"]["csrf"]["csrf"] = $this->csrfToken; $_SESSION["domframework"]["csrf"][$this->csrfToken] = microtime (TRUE);
$_SESSION["domframework"]["csrf"]["csrfStart"] = microtime (TRUE); $GLOBALS["domframework"]["csrf"] = $this;
return $this->csrfToken; return $this->csrfToken;
} }
// }}} // }}}
@@ -65,22 +94,37 @@ class csrf
{ {
if ($this->csrf === FALSE ) if ($this->csrf === FALSE )
return TRUE; 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"]))
{
$_SESSION["domframework"]["csrf"] = array (
$_SESSION["domframework"]["csrf"]["csrf"] =>
$_SESSION["domframework"]["csrf"]["csrfStart"]
);
}
if (! isset ($_SESSION["domframework"]["csrf"]))
{ {
throw new \Exception (dgettext ("domframework", throw new \Exception (dgettext ("domframework",
"No previous CSRF token : abort"), 406); "No previous CSRF token : abort"), 406);
} }
if ($_SESSION["domframework"]["csrf"]["csrf"] !== $tokenFromUser) if (! key_exists ($tokenFromUser, $_SESSION["domframework"]["csrf"]))
{ {
throw new \Exception (dgettext ("domframework", throw new \Exception (dgettext ("domframework",
"Invalid CSRF token provided"), 406); "Invalid CSRF token provided"), 406);
} }
if (($_SESSION["domframework"]["csrf"]["csrfStart"] + $this->csrfTimeout) < if (($_SESSION["domframework"]["csrf"][$tokenFromUser] + $this->csrfTimeout)
microtime (TRUE)) < microtime (TRUE))
{ {
throw new \Exception (dgettext ("domframework", throw new \Exception (dgettext ("domframework",
"Obsolete CSRF token provided"), 406); "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; return TRUE;
} }
// }}} // }}}
@@ -116,7 +160,7 @@ class csrf
// {{{ // {{{
{ {
$this->checkToken ($tokenFromUser); $this->checkToken ($tokenFromUser);
$_SESSION["domframework"]["csrf"]["csrfStart"] = microtime (TRUE); $_SESSION["domframework"]["csrf"][$tokenFromUser] = microtime (TRUE);
return true; return true;
} }
// }}} // }}}