-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpFoundation] Make sessions secure and lazy #24523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -462,11 +462,14 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) | |
->scalarNode('gc_divisor')->end() | ||
->scalarNode('gc_probability')->defaultValue(1)->end() | ||
->scalarNode('gc_maxlifetime')->end() | ||
->booleanNode('use_strict_mode')->end() | ||
->booleanNode('use_strict_mode') | ||
->defaultTrue() | ||
->setDeprecated('The "%path%.%node%" option is enabled by default and deprecated since Symfony 3.4. It will be always enabled in 4.0.') | ||
->end() | ||
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() | ||
->integerNode('metadata_update_threshold') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the info of this option is wrong now
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also as lazy_write is only available since php 7, users with lower php versions will not have lazy write anymore There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. I just added an emulation of lazy writes in the abstract handler for PHP 5. |
||
->defaultValue('0') | ||
->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed') | ||
->info('seconds to wait between 2 session metadata updates') | ||
->end() | ||
->end() | ||
->end() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; | ||
|
||
/** | ||
* This abstract session handler provides a generic implementation | ||
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface, | ||
* enabling strict and lazy session handling. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface | ||
{ | ||
private $sessionName; | ||
private $prefetchId; | ||
private $prefetchData; | ||
private $newSessionId; | ||
private $igbinaryEmptyData; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function open($savePath, $sessionName) | ||
{ | ||
$this->sessionName = $sessionName; | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @param string $sessionId | ||
* | ||
* @return string | ||
*/ | ||
abstract protected function doRead($sessionId); | ||
|
||
/** | ||
* @param string $sessionId | ||
* @param string $data | ||
* | ||
* @return bool | ||
*/ | ||
abstract protected function doWrite($sessionId, $data); | ||
|
||
/** | ||
* @param string $sessionId | ||
* | ||
* @return bool | ||
*/ | ||
abstract protected function doDestroy($sessionId); | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function validateId($sessionId) | ||
{ | ||
$this->prefetchData = $this->read($sessionId); | ||
$this->prefetchId = $sessionId; | ||
|
||
return '' !== $this->prefetchData; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i thought we don't add inheritdoc anymore which seems useless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that was asked for, but rejected AFAIK |
||
*/ | ||
public function read($sessionId) | ||
{ | ||
if (null !== $this->prefetchId) { | ||
$prefetchId = $this->prefetchId; | ||
$prefetchData = $this->prefetchData; | ||
$this->prefetchId = $this->prefetchData = null; | ||
|
||
if ($prefetchId === $sessionId || '' === $prefetchData) { | ||
$this->newSessionId = '' === $prefetchData ? $sessionId : null; | ||
|
||
return $prefetchData; | ||
} | ||
} | ||
|
||
$data = $this->doRead($sessionId); | ||
$this->newSessionId = '' === $data ? $sessionId : null; | ||
if (\PHP_VERSION_ID < 70000) { | ||
$this->prefetchData = $data; | ||
} | ||
|
||
return $data; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function write($sessionId, $data) | ||
{ | ||
if (\PHP_VERSION_ID < 70000 && $this->prefetchData) { | ||
$readData = $this->prefetchData; | ||
$this->prefetchData = null; | ||
|
||
if ($readData === $data) { | ||
return $this->updateTimestamp($sessionId, $data); | ||
} | ||
} | ||
if (null === $this->igbinaryEmptyData) { | ||
// see https://github.com/igbinary/igbinary/issues/146 | ||
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : ''; | ||
} | ||
if ('' === $data || $this->igbinaryEmptyData === $data) { | ||
return $this->destroy($sessionId); | ||
} | ||
$this->newSessionId = null; | ||
|
||
return $this->doWrite($sessionId, $data); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function destroy($sessionId) | ||
{ | ||
if (\PHP_VERSION_ID < 70000) { | ||
$this->prefetchData = null; | ||
} | ||
if (!headers_sent() && ini_get('session.use_cookies')) { | ||
if (!$this->sessionName) { | ||
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this))); | ||
} | ||
$sessionCookie = sprintf(' %s=', urlencode($this->sessionName)); | ||
$sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId)); | ||
$sessionCookieFound = false; | ||
$otherCookies = array(); | ||
foreach (headers_list() as $h) { | ||
if (0 !== stripos($h, 'Set-Cookie:')) { | ||
continue; | ||
} | ||
if (11 === strpos($h, $sessionCookie, 11)) { | ||
$sessionCookieFound = true; | ||
|
||
if (11 !== strpos($h, $sessionCookieWithId, 11)) { | ||
$otherCookies[] = $h; | ||
} | ||
} else { | ||
$otherCookies[] = $h; | ||
} | ||
} | ||
if ($sessionCookieFound) { | ||
header_remove('Set-Cookie'); | ||
foreach ($otherCookies as $h) { | ||
header('Set-Cookie:'.$h, false); | ||
} | ||
} else { | ||
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); | ||
} | ||
} | ||
|
||
return $this->newSessionId === $sessionId || $this->doDestroy($sessionId); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
those or no alternative for WriteCheckSessionHandler. only session.lazy_write option is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
session.lazy_write
requiresupdateTimestamp()
, thusSessionUpdateTimestampHandlerInterface