From a02c5e2d8f840bc3747ed685cc9fbeea87006555 Mon Sep 17 00:00:00 2001 From: Natan Felles Date: Mon, 14 Apr 2025 18:19:02 -0300 Subject: [PATCH 1/2] Allow permanent Set-Cookie --- src/Session.php | 32 +++++++++++++++++++++++++++++++- tests/TestCase.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/Session.php b/src/Session.php index 291fb97..9941921 100644 --- a/src/Session.php +++ b/src/Session.php @@ -96,6 +96,7 @@ protected function setOptions(array $custom) : void // used to auto-regenerate the session id: 'auto_regenerate_maxlifetime' => 0, 'auto_regenerate_destroy' => true, + 'set_cookie_permanent' => false, ]; if (\PHP_VERSION_ID < 80400) { $default['sid_bits_per_character'] = 6; @@ -118,7 +119,8 @@ protected function getOptions(array $custom = []) : array : $this->options; unset( $options['auto_regenerate_maxlifetime'], - $options['auto_regenerate_destroy'] + $options['auto_regenerate_destroy'], + $options['set_cookie_permanent'], ); return $options; } @@ -146,6 +148,7 @@ public function start(array $customOptions = []) : bool ); } $time = \time(); + $this->setPermanentCookie($time); $this->autoRegenerate($time); $this->clearTemp($time); $this->clearFlash(); @@ -169,6 +172,33 @@ public function activate() : bool return $this->start(); } + /** + * @param int $time + * + * @see https://www.php.net/manual/en/function.session-set-cookie-params.php#100657 + * @see https://stackoverflow.com/a/34252812/6027968 + */ + protected function setPermanentCookie(int $time) : void + { + $setCookie = (bool) $this->options['set_cookie_permanent']; + if ($setCookie === false) { + return; + } + $params = \session_get_cookie_params(); + \setcookie( + \session_name(), // @phpstan-ignore-line + \session_id(), // @phpstan-ignore-line + [ // @phpstan-ignore-line + 'expires' => $time + $this->options['cookie_lifetime'], + 'path' => $params['path'], + 'domain' => $params['domain'], + 'secure' => $params['secure'], + 'httponly' => $params['httponly'], + 'samesite' => $params['samesite'], + ] + ); + } + /** * Auto regenerate the session id. * diff --git a/tests/TestCase.php b/tests/TestCase.php index 3b6100b..c9cd463 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -255,4 +255,50 @@ public function testDestroyCookie() : void { self::assertTrue($this->session->destroyCookie()); } + + /** + * By default, a Set-Cookie header is set when the session starts. + * + * @see TestCase::testSetCookiePermanent() + */ + public function testSetOneCookie() : void + { + $this->session->stop(); + $headers = xdebug_get_headers(); + $count = 0; + foreach ($headers as $header) { + if (\str_starts_with($header, 'Set-Cookie: SessionName=')) { + $count++; + } + } + self::assertSame(1, $count); + } + + /** + * The first time the session is started with the set_cookie_permanent + * option, two identical Set-Cookie headers are set. + * + * In future requests the Set-Cookie header is set only once if the request + * contains the Cookie header with the session name and a valid value (but + * this could not be tested). + * + * @see TestCase::testSetOneCookie() + */ + public function testSetCookiePermanent() : void + { + $this->session->stop(); + $this->session = new Session([ + 'name' => 'sess_id', + 'set_cookie_permanent' => 1, + ], $this->handler); + $this->session->start(); + $headers = xdebug_get_headers(); + $count = 0; + foreach ($headers as $header) { + if (\str_starts_with($header, 'Set-Cookie: sess_id=')) { + $count++; + } + } + self::assertSame(2, $count); + } } From c34a00056f74800fdc8617a1fecff6459f6f9ed2 Mon Sep 17 00:00:00 2001 From: Natan Felles Date: Mon, 14 Apr 2025 18:19:27 -0300 Subject: [PATCH 2/2] Update user guide --- guide/index.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/guide/index.rst b/guide/index.rst index f084f8d..f55432f 100644 --- a/guide/index.rst +++ b/guide/index.rst @@ -76,6 +76,14 @@ or by the ``start`` method: 'name' => 'session_id' ]); // bool +Custom Options +^^^^^^^^^^^^^^ + +Custom options only work if they are passed through the Session constructor. + +- `Auto Regenerate ID`_ +- `Set-Cookie Permanent`_ + Auto Regenerate ID ################## @@ -92,6 +100,24 @@ options: 'auto_regenerate_destroy' => true, ])); +This will help avoid +`Session Fixation `_. + +Set-Cookie Permanent +#################### + +It is possible to send the section's Set-Cookie header in all HTTP responses by +setting the ``set_cookie_permanent`` option: + +.. code-block:: php + + $session = new Session([ + 'set_cookie_permanent' => true, + ])); + +This will cause the session cookie expiration date to be updated in the browser +on every response. + Managing Data -------------