Skip to content

Commit 347939c

Browse files
[HttpFoundation] Make sessions secure and lazy
1 parent 3f0a3f5 commit 347939c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+984
-104
lines changed

UPGRADE-3.4.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ Form
126126
FrameworkBundle
127127
---------------
128128

129+
* The `session.use_strict_mode` option has been deprecated and is enabled by default.
130+
129131
* The `cache:clear` command doesn't clear "app" PSR-6 cache pools anymore,
130132
but still clears "system" ones.
131133
Use the `cache:pool:clear` command to clear "app" pools instead.
@@ -235,18 +237,13 @@ HttpFoundation
235237
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`
236238
class has been deprecated and will be removed in 4.0. Use the `\SessionHandler` class instead.
237239

238-
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy` class has been
239-
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
240+
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
241+
deprecated and will be removed in 4.0. Implement `SessionUpdateTimestampHandlerInterface` or
242+
extend `AbstractSessionHandler` instead.
240243

241244
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` class has been
242245
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
243246

244-
* The `Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` class has been
245-
deprecated and will be removed in 4.0. Use your `\SessionHandlerInterface` implementation directly.
246-
247-
* `NativeSessionStorage::setSaveHandler()` now takes an instance of `\SessionHandlerInterface` as argument.
248-
Not passing it is deprecated and will throw a `TypeError` in 4.0.
249-
250247
* Using `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` with the legacy mongo extension
251248
has been deprecated and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.
252249

UPGRADE-4.0.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ Form
329329
FrameworkBundle
330330
---------------
331331

332+
* The `session.use_strict_mode` option has been removed and strict mode is always enabled.
333+
332334
* The `validator.mapping.cache.doctrine.apc` service has been removed.
333335

334336
* The "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter have been removed. Use the `Request::setTrustedProxies()` method in your front controller instead.
@@ -542,12 +544,11 @@ HttpFoundation
542544
* The ability to check only for cacheable HTTP methods using `Request::isMethodSafe()` is
543545
not supported anymore, use `Request::isMethodCacheable()` instead.
544546

545-
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler`,
546-
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy`,
547-
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` and
548-
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy` classes have been removed.
547+
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler` class has been
548+
removed. Implement `SessionUpdateTimestampHandlerInterface` or extend `AbstractSessionHandler` instead.
549549

550-
* `NativeSessionStorage::setSaveHandler()` now requires an instance of `\SessionHandlerInterface` as argument.
550+
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler` and
551+
`Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy` classes have been removed.
551552

552553
* The `Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler` does not work with the legacy
553554
mongo extension anymore. It requires mongodb/mongodb package and ext-mongodb.

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"symfony/polyfill-intl-icu": "~1.0",
3131
"symfony/polyfill-mbstring": "~1.0",
3232
"symfony/polyfill-php56": "~1.0",
33-
"symfony/polyfill-php70": "~1.0",
33+
"symfony/polyfill-php70": "~1.6",
3434
"symfony/polyfill-util": "~1.0"
3535
},
3636
"replace": {

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
3.4.0
55
-----
66

7+
* Session `use_strict_mode` is now enabled by default and the corresponding option has been deprecated
78
* Made the `cache:clear` command to *not* clear "app" PSR-6 cache pools anymore,
89
but to still clear "system" ones; use the `cache:pool:clear` command to clear "app" pools instead
910
* Always register a minimalist logger that writes in `stderr`

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,11 +462,14 @@ private function addSessionSection(ArrayNodeDefinition $rootNode)
462462
->scalarNode('gc_divisor')->end()
463463
->scalarNode('gc_probability')->defaultValue(1)->end()
464464
->scalarNode('gc_maxlifetime')->end()
465-
->booleanNode('use_strict_mode')->end()
465+
->booleanNode('use_strict_mode')
466+
->defaultTrue()
467+
->setDeprecated('The "%path%.%node%" option is enabled by default and deprecated since Symfony 3.4. It will be always enabled in 4.0.')
468+
->end()
466469
->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end()
467470
->integerNode('metadata_update_threshold')
468471
->defaultValue('0')
469-
->info('seconds to wait between 2 session metadata updates, it will also prevent the session handler to write if the session has not changed')
472+
->info('seconds to wait between 2 session metadata updates')
470473
->end()
471474
->end()
472475
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -916,14 +916,7 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c
916916
$container->getDefinition('session.storage.native')->replaceArgument(1, null);
917917
$container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null);
918918
} else {
919-
$handlerId = $config['handler_id'];
920-
921-
if ($config['metadata_update_threshold'] > 0) {
922-
$container->getDefinition('session.handler.write_check')->addArgument(new Reference($handlerId));
923-
$handlerId = 'session.handler.write_check';
924-
}
925-
926-
$container->setAlias('session.handler', $handlerId)->setPrivate(true);
919+
$container->setAlias('session.handler', $config['handler_id'])->setPrivate(true);
927920
}
928921

929922
$container->setParameter('session.save_path', $config['save_path']);

src/Symfony/Bundle/FrameworkBundle/Resources/config/session.xml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,17 @@
4848
<argument type="service" id="session.storage.metadata_bag" />
4949
</service>
5050

51-
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
52-
<argument>%session.save_path%</argument>
51+
<service id="session.handler.native_file" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler">
52+
<argument type="service">
53+
<service class="Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler">
54+
<argument>%session.save_path%</argument>
55+
</service>
56+
</argument>
5357
</service>
5458

55-
<service id="session.handler.write_check" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler" />
59+
<service id="session.handler.write_check" class="Symfony\Component\HttpFoundation\Session\Storage\Handler\WriteCheckSessionHandler">
60+
<deprecated>The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use the `session.lazy_write` ini setting instead.</deprecated>
61+
</service>
5662

5763
<service id="session_listener" class="Symfony\Component\HttpKernel\EventListener\SessionListener">
5864
<tag name="kernel.event_subscriber" />

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ protected static function getBundleDefaultConfig()
301301
'gc_probability' => 1,
302302
'save_path' => '%kernel.cache_dir%/sessions',
303303
'metadata_update_threshold' => '0',
304+
'use_strict_mode' => true,
304305
),
305306
'request' => array(
306307
'enabled' => false,

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ CHANGELOG
44
3.4.0
55
-----
66

7-
* deprecated the `NativeSessionHandler` class,
8-
* deprecated the `AbstractProxy`, `NativeProxy` and `SessionHandlerProxy` classes,
7+
* implemented PHP 7.0's `SessionUpdateTimestampHandlerInterface` with a new
8+
`AbstractSessionHandler` base class and a new `StrictSessionHandler` wrapper
9+
* deprecated the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes
910
* deprecated setting session save handlers that do not implement `\SessionHandlerInterface` in `NativeSessionStorage::setSaveHandler()`
1011
* deprecated using `MongoDbSessionHandler` with the legacy mongo extension; use it with the mongodb/mongodb package and ext-mongodb instead
1112
* deprecated `MemcacheSessionHandler`; use `MemcachedSessionHandler` instead
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
13+
14+
/**
15+
* This abstract session handler provides a generic implementation
16+
* of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
17+
* enabling strict and lazy session handling.
18+
*
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
22+
{
23+
private $sessionName;
24+
private $prefetchId;
25+
private $prefetchData;
26+
private $newSessionId;
27+
private $igbinaryEmptyData;
28+
29+
/**
30+
* {@inheritdoc}
31+
*/
32+
public function open($savePath, $sessionName)
33+
{
34+
$this->sessionName = $sessionName;
35+
36+
return true;
37+
}
38+
39+
/**
40+
* @param string $sessionId
41+
*
42+
* @return string
43+
*/
44+
abstract protected function doRead($sessionId);
45+
46+
/**
47+
* @param string $sessionId
48+
* @param string $data
49+
*
50+
* @return bool
51+
*/
52+
abstract protected function doWrite($sessionId, $data);
53+
54+
/**
55+
* @param string $sessionId
56+
*
57+
* @return bool
58+
*/
59+
abstract protected function doDestroy($sessionId);
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function validateId($sessionId)
65+
{
66+
$this->prefetchData = $this->read($sessionId);
67+
$this->prefetchId = $sessionId;
68+
69+
return '' !== $this->prefetchData;
70+
}
71+
72+
/**
73+
* {@inheritdoc}
74+
*/
75+
public function read($sessionId)
76+
{
77+
if (null !== $this->prefetchId) {
78+
$prefetchId = $this->prefetchId;
79+
$prefetchData = $this->prefetchData;
80+
$this->prefetchId = $this->prefetchData = null;
81+
82+
if ($prefetchId === $sessionId || '' === $prefetchData) {
83+
$this->newSessionId = '' === $prefetchData ? $sessionId : null;
84+
85+
return $prefetchData;
86+
}
87+
}
88+
89+
$data = $this->doRead($sessionId);
90+
$this->newSessionId = '' === $data ? $sessionId : null;
91+
if (\PHP_VERSION_ID < 70000) {
92+
$this->prefetchData = $data;
93+
}
94+
95+
return $data;
96+
}
97+
98+
/**
99+
* {@inheritdoc}
100+
*/
101+
public function write($sessionId, $data)
102+
{
103+
if (\PHP_VERSION_ID < 70000 && $this->prefetchData) {
104+
$readData = $this->prefetchData;
105+
$this->prefetchData = null;
106+
107+
if ($readData === $data) {
108+
return $this->updateTimestamp($sessionId, $data);
109+
}
110+
}
111+
if (null === $this->igbinaryEmptyData) {
112+
// see https://github.com/igbinary/igbinary/issues/146
113+
$this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize(array()) : '';
114+
}
115+
if ('' === $data || $this->igbinaryEmptyData === $data) {
116+
return $this->destroy($sessionId);
117+
}
118+
$this->newSessionId = null;
119+
120+
return $this->doWrite($sessionId, $data);
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
public function destroy($sessionId)
127+
{
128+
if (\PHP_VERSION_ID < 70000) {
129+
$this->prefetchData = null;
130+
}
131+
if (!headers_sent() && ini_get('session.use_cookies')) {
132+
if (!$this->sessionName) {
133+
throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', get_class($this)));
134+
}
135+
$sessionCookie = sprintf(' %s=', urlencode($this->sessionName));
136+
$sessionCookieWithId = sprintf('%s%s;', $sessionCookie, urlencode($sessionId));
137+
$sessionCookieFound = false;
138+
$otherCookies = array();
139+
foreach (headers_list() as $h) {
140+
if (0 !== stripos($h, 'Set-Cookie:')) {
141+
continue;
142+
}
143+
if (11 === strpos($h, $sessionCookie, 11)) {
144+
$sessionCookieFound = true;
145+
146+
if (11 !== strpos($h, $sessionCookieWithId, 11)) {
147+
$otherCookies[] = $h;
148+
}
149+
} else {
150+
$otherCookies[] = $h;
151+
}
152+
}
153+
if ($sessionCookieFound) {
154+
header_remove('Set-Cookie');
155+
foreach ($otherCookies as $h) {
156+
header('Set-Cookie:'.$h, false);
157+
}
158+
} else {
159+
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
160+
}
161+
}
162+
163+
return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
164+
}
165+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy