1
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2026-10-24 12:19:19 +03:00
Files
postfixadmin/model/CsrfToken.php
David Goodwin 8a1f9a11af CSRF protection - support multiple tokens (#985)
possible fix for https://github.com/postfixadmin/postfixadmin/issues/967

CSRF Token changes : rather than have one PFA_Token value in $_SESSION, store a list of them, where each value has a time stamp it was issued on. The aim is to allow the user to have multiple browser tabs open at once - and not have the last tab that loaded trample on others. Tokens expire after 1 hour.

Adds a Smarty helper function `{CSRF_Token}` which will output `<input type=hidden name=CSRF_Token value=asdf123>` or `{CSRF_Token type=url}` which outputs a literal string like 'asdf123' suitable for placing in a URL.

Adds a PHP exception handler in public/common.php, which could be used to display a more friendly error message/response (but at the moment is fairly minimal).

Removes most uses of `die()` within the codebase, and instead moves to throw an exception
2026-03-01 19:30:16 +00:00

46 lines
1.2 KiB
PHP

<?php
class CsrfToken
{
public static function generate(): string
{
if (!isset($_SESSION['CSRF_Tokens']) || !is_array($_SESSION['CSRF_Tokens'])) {
$_SESSION['CSRF_Tokens'] = [];
}
// remove any expired tokens
foreach ($_SESSION['CSRF_Tokens'] as $key => $value) {
if ($value < (time())) {
unset($_SESSION['CSRF_Tokens'][$key]);
}
}
$token = bin2hex(random_bytes(16));
$_SESSION['CSRF_Tokens'][$token] = (time() + 3600); // is an hour an acceptable expiry time?
return $token;
}
/**
* @param string $token
* @return bool
* @throws CsrfInvalidException
*/
public static function assertValid(string $token): bool
{
if (!is_array($_SESSION['CSRF_Tokens'])) {
$_SESSION['CSRF_Tokens'] = [];
}
$value = (int)($_SESSION['CSRF_Tokens'][$token] ?? 0);
// token can only be used once.
unset($_SESSION['CSRF_Tokens'][$token]);
// token cannot have expired
if ($value < time()) {
throw new CsrfInvalidException("Invalid CSRF token - try refreshing the page and try again?");
}
return true;
}
}