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

Skip to content

Commit 774c256

Browse files
[HttpFoundation] make cookies auto-secure when passing them $secure = null
1 parent 2879baf commit 774c256

File tree

7 files changed

+58
-39
lines changed

7 files changed

+58
-39
lines changed

src/Symfony/Component/HttpFoundation/Cookie.php

Lines changed: 16 additions & 3 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';
@@ -72,15 +73,19 @@ public static function fromString($cookie, $decode = false)
7273
* @param int|string|\DateTimeInterface $expire The time the cookie expires
7374
* @param string $path The path on the server in which the cookie will be available on
7475
* @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
76+
* @param bool|null $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client or null to set it later using {@see setSecureDefault()}
7677
* @param bool $httpOnly Whether the cookie will be made accessible only through the HTTP protocol
7778
* @param bool $raw Whether the cookie value should be sent with no url encoding
7879
* @param string|null $sameSite Whether the cookie will be available for cross-site requests
7980
*
8081
* @throws \InvalidArgumentException
8182
*/
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)
83+
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)
8384
{
85+
if (6 > \func_num_args()) {
86+
@trigger_error(sprintf('The default value of the 6th "$secure" argument of "%s"\'s constructor will change from "false" to "null" in Symfony 5.0, you should define its value explicitly when calling it to prevent any unwanted behavior change. Setting it to "null" will auto-enable the "secure" attribute when an HTTPS request comes in.', __METHOD__), E_USER_DEPRECATED);
87+
}
88+
8489
// from PHP source code
8590
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
8691
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
@@ -232,7 +237,7 @@ public function getPath()
232237
*/
233238
public function isSecure()
234239
{
235-
return $this->secure;
240+
return $this->secure ?? $this->secureDefault;
236241
}
237242

238243
/**
@@ -274,4 +279,12 @@ public function getSameSite()
274279
{
275280
return $this->sameSite;
276281
}
282+
283+
/**
284+
* @param bool $default The default value of the "secure" flag when it is set to null
285+
*/
286+
public function setSecureDefault(bool $default): void
287+
{
288+
$this->secureDefault = $default;
289+
}
277290
}

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/Tests/CookieTest.php

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,85 +45,85 @@ public function invalidNames()
4545
*/
4646
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
4747
{
48-
new Cookie($name);
48+
new Cookie($name, null, 0, '/', null, false);
4949
}
5050

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

5959
public function testNegativeExpirationIsNotPossible()
6060
{
61-
$cookie = new Cookie('foo', 'bar', -100);
61+
$cookie = new Cookie('foo', 'bar', -100, '/', null, false);
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 = new Cookie('MyCookie', $value, '/', null, false);
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 = new Cookie('foo', 'bar', 0, '/', null, false);
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 = new Cookie('foo', 'bar', 0, '/', null, false);
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 = new Cookie('foo', 'bar', $expire = time() + 3600, '/', null, false);
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 = new Cookie('foo', 'bar', 3600.9, '/', null, false);
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 = new Cookie('foo', 'bar', $expire, '/', null, false);
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 = new Cookie('foo', 'bar', $expire, '/', null, false);
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 = new Cookie('foo', 'bar', $value, '/', null, false);
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 = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false);
127127

128128
$this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid');
129129
}
@@ -144,26 +144,26 @@ public function testIsHttpOnly()
144144

145145
public function testCookieIsNotCleared()
146146
{
147-
$cookie = new Cookie('foo', 'bar', time() + 3600 * 24);
147+
$cookie = new Cookie('foo', 'bar', time() + 3600 * 24, '/', null, false);
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 = new Cookie('foo', 'bar', time() - 20, '/', null, false);
155155

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

158-
$cookie = new Cookie('foo', 'bar');
158+
$cookie = new Cookie('foo', 'bar', 0, '/', null, false);
159159

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

162-
$cookie = new Cookie('foo', 'bar', 0);
162+
$cookie = new Cookie('foo', 'bar', 0, '/', null, false);
163163

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

166-
$cookie = new Cookie('foo', 'bar', -1);
166+
$cookie = new Cookie('foo', 'bar', -1, '/', null, false);
167167

168168
$this->assertFalse($cookie->isCleared());
169169
}
@@ -176,10 +176,10 @@ public function testToString()
176176
$cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true);
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 = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com', false);
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, '/', '');
182+
$cookie = new Cookie('foo', 'bar', 0, '/', '', false);
183183
$this->assertEquals('foo=bar; path=/; httponly', (string) $cookie);
184184
}
185185

@@ -196,13 +196,13 @@ public function testRawCookie()
196196

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

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

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

src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
$r = require __DIR__.'/common.inc';
66

77
try {
8-
$r->headers->setCookie(new Cookie('Hello + world', 'hodor'));
8+
$r->headers->setCookie(new Cookie('Hello + world', 'hodor', 0, '/', null, false));
99
} catch (\InvalidArgumentException $e) {
1010
echo $e->getMessage();
1111
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function testCacheControlClone()
110110
public function testToStringIncludesCookieHeaders()
111111
{
112112
$bag = new ResponseHeaderBag(array());
113-
$bag->setCookie(new Cookie('foo', 'bar'));
113+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/', null, false));
114114

115115
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
116116

@@ -154,10 +154,10 @@ public function testReplaceWithRemove()
154154
public function testCookiesWithSameNames()
155155
{
156156
$bag = new ResponseHeaderBag();
157-
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
158-
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar'));
159-
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo'));
160-
$bag->setCookie(new Cookie('foo', 'bar'));
157+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar', false));
158+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar', false));
159+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo', false));
160+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/', null, false));
161161

162162
$this->assertCount(4, $bag->getCookies());
163163
$this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie'));
@@ -186,8 +186,8 @@ public function testRemoveCookie()
186186
$bag = new ResponseHeaderBag();
187187
$this->assertFalse($bag->has('set-cookie'));
188188

189-
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar'));
190-
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar'));
189+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar', false));
190+
$bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar', false));
191191
$this->assertTrue($bag->has('set-cookie'));
192192

193193
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
@@ -209,8 +209,8 @@ public function testRemoveCookie()
209209
public function testRemoveCookieWithNullRemove()
210210
{
211211
$bag = new ResponseHeaderBag();
212-
$bag->setCookie(new Cookie('foo', 'bar', 0));
213-
$bag->setCookie(new Cookie('bar', 'foo', 0));
212+
$bag->setCookie(new Cookie('foo', 'bar', 0, '/', null, false));
213+
$bag->setCookie(new Cookie('bar', 'foo', 0, '/', null, false));
214214

215215
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
216216
$this->assertArrayHasKey('/', $cookies['']);

src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ protected function createResponse()
275275
$response->headers->set('Content-Type', 'application/json');
276276
$response->headers->set('X-Foo-Bar', null);
277277
$response->headers->setCookie(new Cookie('foo', 'bar', 1, '/foo', 'localhost', true, true));
278-
$response->headers->setCookie(new Cookie('bar', 'foo', new \DateTime('@946684800')));
279-
$response->headers->setCookie(new Cookie('bazz', 'foo', '2000-12-12'));
278+
$response->headers->setCookie(new Cookie('bar', 'foo', new \DateTime('@946684800'), '/', null, false));
279+
$response->headers->setCookie(new Cookie('bazz', 'foo', '2000-12-12', '/', null, false));
280280

281281
return $response;
282282
}

src/Symfony/Component/Security/Http/Tests/RememberMe/ResponseListenerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class ResponseListenerTest extends TestCase
2424
{
2525
public function testRememberMeCookieIsSentWithResponse()
2626
{
27-
$cookie = new Cookie('rememberme');
27+
$cookie = new Cookie('rememberme', null, 0, '/', null, false);
2828

2929
$request = $this->getRequest(array(
3030
RememberMeServicesInterface::COOKIE_ATTR_NAME => $cookie,
@@ -39,7 +39,7 @@ public function testRememberMeCookieIsSentWithResponse()
3939

4040
public function testRememberMeCookieIsNotSendWithResponseForSubRequests()
4141
{
42-
$cookie = new Cookie('rememberme');
42+
$cookie = new Cookie('rememberme', null, 0, '/', null, false);
4343

4444
$request = $this->getRequest(array(
4545
RememberMeServicesInterface::COOKIE_ATTR_NAME => $cookie,

0 commit comments

Comments
 (0)