From a6963dfc1cbdd0cb78fdcb1c3960d30092b3a91d Mon Sep 17 00:00:00 2001 From: David Coutadeur Date: Fri, 6 Sep 2024 18:19:15 +0200 Subject: [PATCH] symfony cache doc, conf, and improvements (#954) - add upgrade notes, - add cache parameters in config, - remove expired cache entries, - remove some warning messages, - security: always display the same message: invalid token even if the user is not found in ldap, - add more logs, - set an expiration time for each cache entry --- conf/config.inc.php | 12 +++++++++++ docs/config_general.rst | 28 +++++++++++++++++++++++++ docs/config_sms.rst | 9 +++++++++ docs/config_tokens.rst | 5 ++--- docs/upgrade.rst | 20 ++++++++++++++++++ htdocs/index.php | 1 + htdocs/resetbytoken.php | 5 ++++- htdocs/sendsms.php | 15 ++++++++++---- htdocs/sendtoken.php | 45 ++++++++++++++++++++++------------------- 9 files changed, 111 insertions(+), 29 deletions(-) diff --git a/conf/config.inc.php b/conf/config.inc.php index 7e7f54ad..afd72eb5 100644 --- a/conf/config.inc.php +++ b/conf/config.inc.php @@ -247,6 +247,18 @@ # Token lifetime in seconds $token_lifetime = "3600"; +## Cache +# $cache_token_expiration: integer, duration in seconds of cached objects +# each time a token is involved +# (for example when sending a token by sms or by mail) +# it is recommended to set a value >= $token_lifetime +$cache_token_expiration = 3600; +# $cache_form_expiration: integer, duration in seconds of cached objects +# at some steps when a user has to validate a form +# (for example when validating the email address before we send the mail) +# it is recommended to set a value high enough for a user to fill a form +$cache_form_expiration = 120; + # Reset URL (mandatory) $reset_url = "http://ssp.example.com/"; # If inside a virtual host diff --git a/docs/config_general.rst b/docs/config_general.rst index 3e32a60d..dc3e2697 100644 --- a/docs/config_general.rst +++ b/docs/config_general.rst @@ -316,6 +316,34 @@ See `FriendlyCaptcha documentation `_ for mor You can also integrate any other Captcha module by developping the corresponding plugin. (see :doc:`developpers` ) +.. _config_cache: + +Cache +----- + +self-service-password rely on Symfony cache libraries. + +You can define the cache expiration for some objects: + +.. code-block:: php + + $cache_token_expiration = 3600; + +``$cache_token_expiration`` (integer) is the duration in seconds of cached objects each time a token is involved. + +For example when sending a token by sms or by mail, it is the time granted to the user for entering the sms code or for clicking on the link in the mail. + +it is recommended to set a value >= ``$token_lifetime`` + +.. code-block:: php + + $cache_form_expiration = 120; + +``$cache_form_expiration`` (integer) is the duration in seconds of cached objects at some steps when a user has to validate a form. + +For example it is the time granted to a user for validating the email address before sending the mail. It is used mainly for avoiding form replay (by user mistake or by a hacker). + +it is recommended to set a value high enough for a user to fill a form. .. |image0| image:: images/br.png .. |image1| image:: images/catalonia.png diff --git a/docs/config_sms.rst b/docs/config_sms.rst index 079d9502..1a722880 100644 --- a/docs/config_sms.rst +++ b/docs/config_sms.rst @@ -201,3 +201,12 @@ You can also configure the allowed attempts: $sms_max_attempts_token = 3; After these attempts, the sent token is no more valid. + +You should also set a token lifetime, so they are invalid after some time. The +value is in seconds: + +.. code-block:: php + + $token_lifetime = "3600"; + +If you use tokens, you should also set :ref:`config_cache` parameters accordingly. diff --git a/docs/config_tokens.rst b/docs/config_tokens.rst index 898473c3..c2cbfb54 100644 --- a/docs/config_tokens.rst +++ b/docs/config_tokens.rst @@ -50,15 +50,14 @@ You can crypt tokens, to protect the session identifier: .. warning:: If you enable this option, you must change the default value of the security keyphrase. -You should set a token lifetime, so they are deleted if unused. The +You should set a token lifetime, so they are invalid after some time. The value is in seconds: .. code-block:: php $token_lifetime = "3600"; -.. warning:: Token deletion is managed by PHP session garbage - collector. +If you use tokens, you should also set :ref:`config_cache` parameters accordingly. Log --- diff --git a/docs/upgrade.rst b/docs/upgrade.rst index b02e1840..5d857bf8 100644 --- a/docs/upgrade.rst +++ b/docs/upgrade.rst @@ -1,6 +1,26 @@ Upgrade ======= +From 1.6 to 1.7 +--------------- + +If you have configured ``$token_lifetime`` parameter, for example for reset by sms or reset by mail features, you should verify that the duration is coherent with the new cache parameters, and adapt these parameters in your local configuration file if needed: + +.. code-block:: php + + # $cache_token_expiration: integer, duration in seconds of cached objects + # each time a token is involved + # (for example when sending a token by sms or by mail) + # it is recommended to set a value >= $token_lifetime + $cache_token_expiration = 3600; + # $cache_form_expiration: integer, duration in seconds of cached objects + # at some steps when a user has to validate a form + # (for example when validating the email address before we send the mail) + # it is recommended to set a value high enough for a user to fill a form + $cache_form_expiration = 120; + + + From 1.5 to 1.6 --------------- diff --git a/htdocs/index.php b/htdocs/index.php index b67585b4..7813b0ff 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -129,6 +129,7 @@ $defaultLifetime = 0, $directory = null ); +$sspCache->prune(); #============================================================================== # Captcha Config diff --git a/htdocs/resetbytoken.php b/htdocs/resetbytoken.php index ab7cffb4..bf0f4f70 100644 --- a/htdocs/resetbytoken.php +++ b/htdocs/resetbytoken.php @@ -56,7 +56,10 @@ # will gather login,time and smstoken values from session. $cached_token = $sspCache->getItem($tokenid); $cached_token_content = $cached_token->get(); - $login = $cached_token_content['login']; + if($cached_token->isHit()) + { + $login = $cached_token_content['login']; + } $smstoken = isset($cached_token_content['smstoken']) ? $cached_token_content['smstoken'] : false; $posttoken = isset($_REQUEST['smstoken']) ? $_REQUEST['smstoken'] : 'undefined'; diff --git a/htdocs/sendsms.php b/htdocs/sendsms.php index fdf3e5c4..84581889 100644 --- a/htdocs/sendsms.php +++ b/htdocs/sendsms.php @@ -87,9 +87,12 @@ $cached_token = $sspCache->getItem($tokenid); $cached_token_content = $cached_token->get(); - $login = $cached_token_content['login']; - $sessiontoken = $cached_token_content['smstoken']; - $attempts = $cached_token_content['attempts']; + if($cached_token->isHit()) + { + $login = $cached_token_content['login']; + $sessiontoken = $cached_token_content['smstoken']; + $attempts = $cached_token_content['attempts']; + } if (!$login or !$sessiontoken) { list($result, $token) = obscure_info_sendsms("tokenattempts", @@ -97,7 +100,7 @@ $token, $obscure_notfound_sendsms, $keyphrase); - error_log("Unable to open session $smstokenid"); + error_log("Unable to open session $tokenid"); } elseif ($sessiontoken != $smstoken) { # To have only x tries and not x+1 tries if ($attempts < ($sms_max_attempts_token - 1)) { @@ -211,7 +214,9 @@ 'time' => time(), 'attempts' => 0 ]); + $smscached_token->expiresAfter($cache_token_expiration); $sspCache->save($smscached_token); + error_log("generated cache entry with id: " . $smstoken_session_id. " for storing step 'send sms' of password reset by sms workflow, valid for $cache_token_expiration s"); $data = array( "sms_attribute" => $sms, "smsresetmessage" => $messages['smsresetmessage'], "smstoken" => $smstoken) ; @@ -271,7 +276,9 @@ 'time' => time(), 'smstoken' => $smstoken ]); + $smscached_token->expiresAfter($cache_form_expiration); $sspCache->save($smscached_token); + error_log("generated cache entry with id: " . $smstoken_session_id. " for storing step 'password change' of password reset by sms workflow, valid for $cache_form_expiration s"); $token = encrypt($smstoken_session_id, $keyphrase); diff --git a/htdocs/sendtoken.php b/htdocs/sendtoken.php index 7ca91b31..e1377102 100644 --- a/htdocs/sendtoken.php +++ b/htdocs/sendtoken.php @@ -61,8 +61,9 @@ $formtoken = hash('sha256', bin2hex(random_bytes(16))); $cachedToken = $sspCache->getItem($formtoken); $cachedToken->set($formtoken); + $cachedToken->expiresAfter($cache_form_expiration); $sspCache->save($cachedToken); - error_log("generated form token: " . $formtoken); + error_log("generated form token: " . $formtoken . " valid for $cache_form_expiration s"); } # Check the entered username for characters that our installation doesn't support @@ -70,6 +71,25 @@ $result = check_username_validity($login,$login_forbidden_chars); } +#============================================================================== +# Check tokenform +#============================================================================== + +if ( !$result ) { + $formtoken = strval($_REQUEST["formtoken"]); + $cachedToken = $sspCache->getItem($formtoken); + if( $cachedToken->get() == $formtoken ) + { + # Remove session + $sspCache->deleteItem($formtoken); + } + else + { + error_log("Invalid form token: sent: $formtoken, stored: " . $cachedToken->get()); + $result = "invalidformtoken"; + } +} + #============================================================================== # Check captcha #============================================================================== @@ -162,7 +182,7 @@ #============================================================================== if ( !$result ) { - # Use cache to register token + # Use cache to register token sent by mail $token_session_id = hash('sha256', bin2hex(random_bytes(16))); if ( $crypt_tokens ) { $token = encrypt($token_session_id, $keyphrase); @@ -174,29 +194,12 @@ 'login' => $login, 'time' => time() ]); + $cached_token->expiresAfter($cache_token_expiration); $sspCache->save($cached_token); + error_log("generated cache entry with id: " . $token_session_id. " for storing password reset by mail workflow, valid for $cache_token_expiration s"); } -#============================================================================== -# Check tokenform -#============================================================================== - -if ( !$result ) { - $formtoken = strval($_REQUEST["formtoken"]); - $cachedToken = $sspCache->getItem($formtoken); - if( $cachedToken->get() == $formtoken ) - { - # Remove session - $sspCache->deleteItem($formtoken); - } - else - { - error_log("Invalid form token: sent: $formtoken, stored: " . $cachedToken->get()); - $result = "invalidformtoken"; - } -} - #============================================================================== # Send token by mail #==============================================================================