Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 42a7546

Browse files
[HttpFoundation] make cookies auto-secure when passing them $secure=null + plan to make it and samesite=lax the defaults in 5.0
1 parent 00e5cd9 commit 42a7546

23 files changed

+172
-79
lines changed

UPGRADE-4.2.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ Form
7272
{% endfor %}
7373
```
7474

75+
HttpFoundation
76+
--------------
77+
78+
* The default value of the "$secure" and "$samesite" arguments of Cookie's constructor
79+
will respectively change from "false" to "null" and from "null" to "lax" in Symfony
80+
5.0, you should define their values explicitly or use "Cookie::create()" instead.
81+
7582
Process
7683
-------
7784

UPGRADE-5.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ HttpFoundation
117117
* The `$size` argument of the `UploadedFile` constructor has been removed.
118118
* The `getClientSize()` method of the `UploadedFile` class has been removed.
119119
* The `getSession()` method of the `Request` class throws an exception when session is null.
120+
* The default value of the "$secure" and "$samesite" arguments of Cookie's constructor
121+
changed respectively from "false" to "null" and from "null" to "lax".
120122

121123
Monolog
122124
-------

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ public function load(array $configs, ContainerBuilder $container)
199199
if ($this->isConfigEnabled($container, $config['session'])) {
200200
$this->sessionConfigEnabled = true;
201201
$this->registerSessionConfiguration($config['session'], $container, $loader);
202+
if (!empty($config['test'])) {
203+
$container->getDefinition('test.session.listener')->setArgument(1, '%session.storage.options%');
204+
}
202205
}
203206

204207
if ($this->isConfigEnabled($container, $config['request'])) {

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ CHANGELOG
55
-----
66

77
* added `getAcceptableFormats()` for reading acceptable formats based on Accept header
8+
* the default value of the "$secure" and "$samesite" arguments of Cookie's constructor
9+
will respectively change from "false" to "null" and from "null" to "lax" in Symfony
10+
5.0, you should define their values explicitly or use "Cookie::create()" instead.
811

912
4.1.3
1013
-----

src/Symfony/Component/HttpFoundation/Cookie.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Cookie
2727
protected $httpOnly;
2828
private $raw;
2929
private $sameSite;
30+
private $secureDefault = false;
3031

3132
const SAMESITE_LAX = 'lax';
3233
const SAMESITE_STRICT = 'strict';
@@ -66,21 +67,30 @@ public static function fromString($cookie, $decode = false)
6667
return new static($name, $value, $data['expires'], $data['path'], $data['domain'], $data['secure'], $data['httponly'], $data['raw'], $data['samesite']);
6768
}
6869

70+
public static function create(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = null, bool $httpOnly = true, bool $raw = false, ?string $sameSite = self::SAMESITE_LAX): self
71+
{
72+
return new self($name, $value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
73+
}
74+
6975
/**
7076
* @param string $name The name of the cookie
7177
* @param string|null $value The value of the cookie
7278
* @param int|string|\DateTimeInterface $expire The time the cookie expires
7379
* @param string $path The path on the server in which the cookie will be available on
7480
* @param string|null $domain The domain that the cookie is available to
75-
* @param bool $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client
81+
* @param bool|null $secure Whether the client should send back the cookie only over HTTPS or null to auto-enable this when the request is already using HTTPS
7682
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
7783
* @param bool $raw Whether the cookie value should be sent with no url encoding
7884
* @param string|null $sameSite Whether the cookie will be available for cross-site requests
7985
*
8086
* @throws \InvalidArgumentException
8187
*/
82-
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
88+
public function __construct(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, ?bool $secure = false, bool $httpOnly = true, bool $raw = false, string $sameSite = null)
8389
{
90+
if (9 > \func_num_args()) {
91+
@trigger_error(sprintf('The default value of the "$secure" and "$samesite" arguments of "%s"\'s constructor will respectively change from "false" to "null" and from "null" to "lax" in Symfony 5.0, you should define their values explicitly or use "Cookie::create()" instead.', __METHOD__), E_USER_DEPRECATED);
92+
}
93+
8494
// from PHP source code
8595
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
8696
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
@@ -110,7 +120,9 @@ public function __construct(string $name, string $value = null, $expire = 0, ?st
110120
$this->httpOnly = $httpOnly;
111121
$this->raw = $raw;
112122

113-
if (null !== $sameSite) {
123+
if ('' === $sameSite) {
124+
$sameSite = null;
125+
} elseif (null !== $sameSite) {
114126
$sameSite = strtolower($sameSite);
115127
}
116128

@@ -232,7 +244,7 @@ public function getPath()
232244
*/
233245
public function isSecure()
234246
{
235-
return $this->secure;
247+
return $this->secure ?? $this->secureDefault;
236248
}
237249

238250
/**
@@ -274,4 +286,12 @@ public function getSameSite()
274286
{
275287
return $this->sameSite;
276288
}
289+
290+
/**
291+
* @param bool $default The default value of the "secure" flag when it is set to null
292+
*/
293+
public function setSecureDefault(bool $default): void
294+
{
295+
$this->secureDefault = $default;
296+
}
277297
}

src/Symfony/Component/HttpFoundation/Response.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ public function prepare(Request $request)
313313

314314
$this->ensureIEOverSSLCompatibility($request);
315315

316+
if ($request->isSecure()) {
317+
foreach ($headers->getCookies() as $cookie) {
318+
$cookie->setSecureDefault(true);
319+
}
320+
}
321+
316322
return $this;
317323
}
318324

src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public function getCookies($format = self::COOKIES_FLAT)
247247
*/
248248
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
249249
{
250-
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly));
250+
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
251251
}
252252

253253
/**

src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ public function destroy($sessionId)
128128
if (\PHP_VERSION_ID < 70300) {
129129
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'));
130130
} else {
131-
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly'), ini_get('session.cookie_samesite'));
131+
$params = session_get_cookie_params();
132+
$params['lifetime'] = 0;
133+
setcookie($this->sessionName, '', $params);
132134
}
133135
}
134136
}

src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public function start()
153153
if (null !== $this->emulateSameSite) {
154154
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
155155
if (null !== $originalCookie) {
156-
header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite));
156+
header(sprintf('%s; samesite=%s', $originalCookie, $this->emulateSameSite));
157157
}
158158
}
159159

src/Symfony/Component/HttpFoundation/Tests/CookieTest.php

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -45,177 +45,177 @@ public function invalidNames()
4545
*/
4646
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
4747
{
48-
new Cookie($name);
48+
Cookie::create($name);
4949
}
5050

5151
/**
5252
* @expectedException \InvalidArgumentException
5353
*/
5454
public function testInvalidExpiration()
5555
{
56-
new Cookie('MyCookie', 'foo', 'bar');
56+
Cookie::create('MyCookie', 'foo', 'bar');
5757
}
5858

5959
public function testNegativeExpirationIsNotPossible()
6060
{
61-
$cookie = new Cookie('foo', 'bar', -100);
61+
$cookie = Cookie::create('foo', 'bar', -100);
6262

6363
$this->assertSame(0, $cookie->getExpiresTime());
6464
}
6565

6666
public function testGetValue()
6767
{
6868
$value = 'MyValue';
69-
$cookie = new Cookie('MyCookie', $value);
69+
$cookie = Cookie::create('MyCookie', $value);
7070

7171
$this->assertSame($value, $cookie->getValue(), '->getValue() returns the proper value');
7272
}
7373

7474
public function testGetPath()
7575
{
76-
$cookie = new Cookie('foo', 'bar');
76+
$cookie = Cookie::create('foo', 'bar');
7777

7878
$this->assertSame('/', $cookie->getPath(), '->getPath() returns / as the default path');
7979
}
8080

8181
public function testGetExpiresTime()
8282
{
83-
$cookie = new Cookie('foo', 'bar');
83+
$cookie = Cookie::create('foo', 'bar');
8484

8585
$this->assertEquals(0, $cookie->getExpiresTime(), '->getExpiresTime() returns the default expire date');
8686

87-
$cookie = new Cookie('foo', 'bar', $expire = time() + 3600);
87+
$cookie = Cookie::create('foo', 'bar', $expire = time() + 3600);
8888

8989
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
9090
}
9191

9292
public function testGetExpiresTimeIsCastToInt()
9393
{
94-
$cookie = new Cookie('foo', 'bar', 3600.9);
94+
$cookie = Cookie::create('foo', 'bar', 3600.9);
9595

9696
$this->assertSame(3600, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date as an integer');
9797
}
9898

9999
public function testConstructorWithDateTime()
100100
{
101101
$expire = new \DateTime();
102-
$cookie = new Cookie('foo', 'bar', $expire);
102+
$cookie = Cookie::create('foo', 'bar', $expire);
103103

104104
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
105105
}
106106

107107
public function testConstructorWithDateTimeImmutable()
108108
{
109109
$expire = new \DateTimeImmutable();
110-
$cookie = new Cookie('foo', 'bar', $expire);
110+
$cookie = Cookie::create('foo', 'bar', $expire);
111111

112112
$this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date');
113113
}
114114

115115
public function testGetExpiresTimeWithStringValue()
116116
{
117117
$value = '+1 day';
118-
$cookie = new Cookie('foo', 'bar', $value);
118+
$cookie = Cookie::create('foo', 'bar', $value);
119119
$expire = strtotime($value);
120120

121121
$this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1);
122122
}
123123

124124
public function testGetDomain()
125125
{
126-
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com');
126+
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com');
127127

128128
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
129129
}
130130

131131
public function testIsSecure()
132132
{
133-
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true);
133+
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', true);
134134

135135
$this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS');
136136
}
137137

138138
public function testIsHttpOnly()
139139
{
140-
$cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
140+
$cookie = Cookie::create('foo', 'bar', 0, '/', '.myfoodomain.com', false, true);
141141

142142
$this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP');
143143
}
144144

145145
public function testCookieIsNotCleared()
146146
{
147-
$cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
147+
$cookie = Cookie::create('foo', 'bar', time() + 3600 * 24);
148148

149149
$this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet');
150150
}
151151

152152
public function testCookieIsCleared()
153153
{
154-
$cookie = new Cookie('foo', 'bar', time() - 20);
154+
$cookie = Cookie::create('foo', 'bar', time() - 20);
155155

156156
$this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired');
157157

158-
$cookie = new Cookie('foo', 'bar');
158+
$cookie = Cookie::create('foo', 'bar');
159159

160160
$this->assertFalse($cookie->isCleared());
161161

162-
$cookie = new Cookie('foo', 'bar', 0);
162+
$cookie = Cookie::create('foo', 'bar');
163163

164164
$this->assertFalse($cookie->isCleared());
165165

166-
$cookie = new Cookie('foo', 'bar', -1);
166+
$cookie = Cookie::create('foo', 'bar', -1);
167167

168168
$this->assertFalse($cookie->isCleared());
169169
}
170170

171171
public function testToString()
172172
{
173-
$cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
173+
$cookie = Cookie::create('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
174174
$this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie');
175175

176-
$cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
176+
$cookie = Cookie::create('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, false, null);
177177
$this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)');
178178

179-
$cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com');
179+
$cookie = Cookie::create('foo', null, 1, '/admin/', '.myfoodomain.com', false, true, false, null);
180180
$this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL');
181181

182-
$cookie = new Cookie('foo', 'bar', 0, '/', '');
183-
$this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
182+
$cookie = Cookie::create('foo', 'bar');
183+
$this->assertEquals('foo=bar; path=/; httponly; samesite=lax', (string) $cookie);
184184
}
185185

186186
public function testRawCookie()
187187
{
188-
$cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false);
188+
$cookie = Cookie::create('foo', 'b a r', 0, '/', null, false, false, false, null);
189189
$this->assertFalse($cookie->isRaw());
190190
$this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie);
191191

192-
$cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true);
192+
$cookie = Cookie::create('foo', 'b+a+r', 0, '/', null, false, false, true, null);
193193
$this->assertTrue($cookie->isRaw());
194194
$this->assertEquals('foo=b+a+r; path=/', (string) $cookie);
195195
}
196196

197197
public function testGetMaxAge()
198198
{
199-
$cookie = new Cookie('foo', 'bar');
199+
$cookie = Cookie::create('foo', 'bar');
200200
$this->assertEquals(0, $cookie->getMaxAge());
201201

202-
$cookie = new Cookie('foo', 'bar', $expire = time() + 100);
202+
$cookie = Cookie::create('foo', 'bar', $expire = time() + 100);
203203
$this->assertEquals($expire - time(), $cookie->getMaxAge());
204204

205-
$cookie = new Cookie('foo', 'bar', $expire = time() - 100);
205+
$cookie = Cookie::create('foo', 'bar', $expire = time() - 100);
206206
$this->assertEquals(0, $cookie->getMaxAge());
207207
}
208208

209209
public function testFromString()
210210
{
211211
$cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly');
212-
$this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie);
212+
$this->assertEquals(Cookie::create('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true, null), $cookie);
213213

214214
$cookie = Cookie::fromString('foo=bar', true);
215-
$this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie);
215+
$this->assertEquals(Cookie::create('foo', 'bar', 0, '/', null, false, false, false, null), $cookie);
216216

217217
$cookie = Cookie::fromString('foo', true);
218-
$this->assertEquals(new Cookie('foo', null, 0, '/', null, false, false), $cookie);
218+
$this->assertEquals(Cookie::create('foo', null, 0, '/', null, false, false, false, null), $cookie);
219219
}
220220

221221
public function testFromStringWithHttpOnly()
@@ -227,9 +227,27 @@ public function testFromStringWithHttpOnly()
227227
$this->assertFalse($cookie->isHttpOnly());
228228
}
229229

230-
public function testSameSiteAttributeIsCaseInsensitive()
230+
public function testSameSiteAttribute()
231231
{
232232
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax');
233233
$this->assertEquals('lax', $cookie->getSameSite());
234+
235+
$cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, '');
236+
$this->assertNull($cookie->getSameSite());
237+
}
238+
239+
public function testSetSecureDefault()
240+
{
241+
$cookie = Cookie::create('foo', 'bar');
242+
243+
$this->assertFalse($cookie->isSecure());
244+
245+
$cookie->setSecureDefault(true);
246+
247+
$this->assertTrue($cookie->isSecure());
248+
249+
$cookie->setSecureDefault(false);
250+
251+
$this->assertFalse($cookie->isSecure());
234252
}
235253
}

0 commit comments

Comments
 (0)