Skip to content

Commit aa86b9d

Browse files
committed
Response: setCookie() forces Secure for SameSite=None
A cookie with SameSite=None is rejected by browsers unless it also carries the Secure attribute (RFC 6265bis). setCookie() now enables Secure automatically in that case, overriding both the $secure argument and the cookieSecure default.
1 parent 5ce7f08 commit aa86b9d

2 files changed

Lines changed: 14 additions & 2 deletions

File tree

src/Http/Response.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,19 @@ public function setCookie(
257257
}
258258

259259
$seconds = Helpers::expirationToSeconds($expire);
260+
$sameSite ??= self::SameSiteLax;
261+
$secure = $sameSite === self::SameSiteNone
262+
? true // SameSite=None requires the Secure attribute, otherwise the browser rejects the cookie
263+
: $secure ?? $this->cookieSecure;
260264
// the value is raw-url-encoded the same way PHP reads it back from $_COOKIE;
261265
// Max-Age takes precedence over expires (RFC 6265), expires is sent too for ancient clients
262266
$cookie = $name . '=' . rawurlencode($value)
263267
. ($seconds === null ? '' : '; expires=' . Helpers::formatDate(time() + $seconds) . '; Max-Age=' . max(0, $seconds))
264268
. '; path=' . $path
265269
. ($domain === '' ? '' : '; domain=' . $domain)
266-
. (($secure ?? $this->cookieSecure) ? '; secure' : '')
270+
. ($secure ? '; secure' : '')
267271
. (($httpOnly ?? true) ? '; HttpOnly' : '')
268-
. '; SameSite=' . ($sameSite ?? self::SameSiteLax);
272+
. '; SameSite=' . $sameSite;
269273
header('Set-Cookie: ' . $cookie, replace: false);
270274
return $this;
271275
}

tests/Http/Response.setCookie.phpt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ Assert::exception(
135135
);
136136

137137

138+
// SameSite=None forces the Secure attribute (otherwise the browser rejects the cookie)
139+
$response = new Http\Response;
140+
$old = headers_list();
141+
$response->setCookie('test', 'value', null, sameSite: Http\IResponse::SameSiteNone);
142+
$headers = array_values(array_diff(headers_list(), $old, ['Set-Cookie:']));
143+
Assert::same(['Set-Cookie: test=value; path=/; secure; HttpOnly; SameSite=None'], $headers);
144+
145+
138146
// integer 0 is deprecated, but kept as a session cookie for BC
139147
$response = new Http\Response;
140148
$old = headers_list();

0 commit comments

Comments
 (0)