From db4dca59a293d02e2bbdd437f2aca35c3964cf73 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 1 Dec 2020 15:08:29 +0100 Subject: [PATCH] Add reauthentication logic to SessionHandler --- .../system/session/SessionHandler.class.php | 83 ++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php index 739a7db766..24a0af976f 100644 --- a/wcfsetup/install/files/lib/system/session/SessionHandler.class.php +++ b/wcfsetup/install/files/lib/system/session/SessionHandler.class.php @@ -137,9 +137,14 @@ final class SessionHandler extends SingletonFactory { private const GUEST_SESSION_LIFETIME = 2 * 3600; private const USER_SESSION_LIFETIME = 14 * 86400; - private const CHANGE_USER_AFTER_MULTIFACTOR_KEY = '__changeUserAfterMultifactor__'; + private const CHANGE_USER_AFTER_MULTIFACTOR_KEY = self::class."\0__changeUserAfterMultifactor__"; private const PENDING_USER_LIFETIME = 15 * 60; + private const REAUTHENTICATION_KEY = self::class."\0__reauthentication__"; + private const REAUTHENTICATION_HARD_LIMIT = 12 * 3600; + private const REAUTHENTICATION_SOFT_LIMIT = 2 * 3600; + private const REAUTHENTICATION_GRACE_PERIOD = 15 * 60; + /** * Provides access to session data. * @@ -858,6 +863,82 @@ final class SessionHandler extends SingletonFactory { } } + /** + * Checks whether the user needs to authenticate themselves once again + * to access a security critical area. + * + * If `true` is returned you should perform a redirect to `ReAuthenticationForm`, + * otherwise the user is sufficiently authenticated and may proceed. + * + * @throws \BadMethodCallException If the current user is a guest. + */ + public function needsReauthentication(): bool { + if (!$this->getUser()->userID) { + throw new \BadMethodCallException('The current user is a guest.'); + } + + $data = $this->getVar(self::REAUTHENTICATION_KEY); + + // Request a new authentication if no stored information is available. + if (!$data) { + return true; + } + + $lastAuthentication = $data['lastAuthentication']; + $lastCheck = $data['lastCheck']; + + // Request a new authentication if the hard limit since the last authentication + // is exceeded. + if ($lastAuthentication < (TIME_NOW - self::REAUTHENTICATION_HARD_LIMIT)) { + return true; + } + + // Request a new authentication if the soft limit since the last authentication + // is exceeded ... + if ($lastAuthentication < (TIME_NOW - self::REAUTHENTICATION_SOFT_LIMIT)) { + // ... and the grace period since the last check is also exceeded. + if ($lastCheck < (TIME_NOW - self::REAUTHENTICATION_GRACE_PERIOD)) { + return true; + } + } + + // If we reach this point we determined that a new authentication is not necessary. + \assert( + ($lastAuthentication >= TIME_NOW - self::REAUTHENTICATION_SOFT_LIMIT) || + ($lastAuthentication >= TIME_NOW - self::REAUTHENTICATION_HARD_LIMIT && + $lastCheck >= TIME_NOW - self::REAUTHENTICATION_GRACE_PERIOD) + ); + + // Update the lastCheck timestamp to make sure that the grace period works properly. + // + // The grace period allows the user to complete their action if the soft limit + // expires between loading a form and actually submitting that form, provided that + // the user does not take longer than the grace period to fill in the form. + $data['lastCheck'] = TIME_NOW; + $this->register(self::REAUTHENTICATION_KEY, $data); + + return false; + } + + /** + * Registers that the user performed reauthentication successfully. + * + * This method should be considered to be semi-public and is intended to be used + * by `ReAuthenticationForm` only. + * + * @throws \BadMethodCallException If the current user is a guest. + */ + public function registerReauthentication(): void { + if (!$this->getUser()->userID) { + throw new \BadMethodCallException('The current user is a guest.'); + } + + $this->register(self::REAUTHENTICATION_KEY, [ + 'lastAuthentication' => TIME_NOW, + 'lastCheck' => TIME_NOW, + ]); + } + /** * Updates user session on shutdown. */ -- 2.20.1