diff --git a/core/lib/Drupal/Core/Session/MetadataBag.php b/core/lib/Drupal/Core/Session/MetadataBag.php index 57d20583e8..46bacc01ae 100644 --- a/core/lib/Drupal/Core/Session/MetadataBag.php +++ b/core/lib/Drupal/Core/Session/MetadataBag.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Session; +use Drupal\Component\Utility\Crypt; use Drupal\Core\Site\Settings; use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag as SymfonyMetadataBag; @@ -53,15 +54,17 @@ public function getCsrfTokenSeed() { */ public function stampNew($lifetime = NULL) { parent::stampNew($lifetime); - unset($this->meta[static::CSRF_TOKEN_SEED]); - } + // We set token seed immediately to avoid race condition between two + // simultaneous requests without a seed. + $this->setCsrfTokenSeed(Crypt::randomBytesBase64()); + } /** * Clear the CSRF token seed. */ public function clearCsrfTokenSeed() { - @trigger_error('Calling ' . __METHOD__ . '() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. Use $this->clearCsrfTokenSeed instead. See https://www.drupal.org/node/3187914', E_USER_DEPRECATED); + @trigger_error('Calling ' . __METHOD__ . '() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. Use \Drupal\Core\Session\MetadataBag::stampNew() instead. See https://www.drupal.org/node/3187914', E_USER_DEPRECATED); unset($this->meta[static::CSRF_TOKEN_SEED]); } diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php index 4b865201c1..fc07231441 100644 --- a/core/lib/Drupal/Core/Session/SessionManager.php +++ b/core/lib/Drupal/Core/Session/SessionManager.php @@ -140,19 +140,16 @@ public function start() { public function getId() { $id = $this->saveHandler->getId(); - // Some code might rely on the existence of a session ID, before we even had - // a real session, which was supported by previous versions. - // We generate a random session ID here, but also throw a deprecation - // as you should never get to this point. if (empty($id)) { - // Randomly generate a session identifier for this request. + // Legacy code might rely on the existence of a session ID before a real + // session exists. In this case, generate a random session ID to provide + // backwards compatibility. @trigger_error('Calling ' . __METHOD__ . '() outside of an actual existing session is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. This is often used for anonymous users. See https://www.drupal.org/node/3006306', E_USER_DEPRECATED); $this->setId(Crypt::randomBytesBase64()); } return $this->saveHandler->getId(); } - /** * Forcibly start a PHP session. * @@ -218,11 +215,16 @@ public function regenerate($destroy = FALSE, $lifetime = NULL) { return; } - // We set token seed immediately to avoid race condition between two - // simultaneous requests without a seed. - $this->getMetadataBag()->setCsrfTokenSeed(Crypt::randomBytesBase64()); + $regenerated = parent::regenerate($destroy, $lifetime); + + if (!$regenerated && $destroy) { + // Ensure the metadata bag has been stamped. If the parent::regenerate() + // is called prior to the session being started it will not refresh the + // metadata as expected. + $this->getMetadataBag()->stampNew($lifetime); + } - return parent::regenerate($destroy, $lifetime); + return $regenerated; } /** diff --git a/core/tests/Drupal/Tests/Core/Session/MetadataBagTest.php b/core/tests/Drupal/Tests/Core/Session/MetadataBagTest.php index 2f9728c0b5..91048d2ebc 100644 --- a/core/tests/Drupal/Tests/Core/Session/MetadataBagTest.php +++ b/core/tests/Drupal/Tests/Core/Session/MetadataBagTest.php @@ -8,18 +8,29 @@ /** * @coversDefaultClass \Drupal\Core\Session\MetadataBag + * @group Session */ class MetadataBagTest extends UnitTestCase { /** + * @covers ::stampNew + */ + public function testStampNew() { + $metadata = new MetadataBag(new Settings([])); + $metadata->setCsrfTokenSeed('a_cryptographically_secure_long_random_string_should_used_here'); + $metadata->stampNew(); + $this->assertNotEquals('a_cryptographically_secure_long_random_string_should_used_here', $metadata->getCsrfTokenSeed()); + } + + /** + * @covers ::clearCsrfTokenSeed * @group legacy */ public function testDeprecatedClearCsrfTokenSeed() { - $this->expectDeprecation('Calling Drupal\Core\Session\MetadataBag::clearCsrfTokenSeed() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. Use $this->clearCsrfTokenSeed instead. See https://www.drupal.org/node/3187914'); + $this->expectDeprecation('Calling Drupal\Core\Session\MetadataBag::clearCsrfTokenSeed() is deprecated in drupal:9.2.0 and will be removed in drupal:10.0.0. Use \Drupal\Core\Session\MetadataBag::stampNew() instead. See https://www.drupal.org/node/3187914'); $metadata = new MetadataBag(new Settings([])); $metadata->clearCsrfTokenSeed(); } - } diff --git a/core/tests/Drupal/Tests/Core/Session/SessionManagerTest.php b/core/tests/Drupal/Tests/Core/Session/SessionManagerTest.php index 7aaedf6778..39ba7946bf 100644 --- a/core/tests/Drupal/Tests/Core/Session/SessionManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Session/SessionManagerTest.php @@ -11,6 +11,10 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +/** + * @coversDefaultClass \Drupal\Core\Session\SessionManager + * @group Session + */ class SessionManagerTest extends UnitTestCase { /** @@ -50,5 +54,4 @@ public function getId() { return $this->id; } - }