Skip to content

Commit 8404a77

Browse files
committed
ext/session: reject cookie separators in cookie_path and cookie_domain
php_session_send_cookie() appends session.cookie_path and session.cookie_domain to the Set-Cookie header verbatim, and neither setting rejected the separators that terminate a cookie attribute. An application could therefore append its own Domain, Path, HttpOnly, Secure, or SameSite attribute through either value. Apply the character set setcookie() already enforces on its $path and $domain options.
1 parent 78fc4cd commit 8404a77

3 files changed

Lines changed: 66 additions & 2 deletions

File tree

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ PHP 8.6 UPGRADE NOTES
121121
unchanged. Previously, NUL bytes were silently accepted: for cookie_path
122122
and cookie_domain this caused the SAPI to drop the Set-Cookie header; for
123123
cache_limiter the value was silently truncated at the NUL byte.
124+
. Setting session.cookie_path or session.cookie_domain to a value containing
125+
one of ",; \t\r\n\013\014" now emits a warning and leaves the setting
126+
unchanged, matching the validation setcookie() already applies to its
127+
$path and $domain options. Previously such a value was appended to the
128+
Set-Cookie header verbatim, which let an application terminate one
129+
attribute and append further ones.
124130
. A ValueError is not thrown if $name is a string containing NUL bytes in
125131
session_module_name().
126132
. session_encode() now returns an empty string instead of false for empty

ext/session/session.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ zend_class_entry *php_session_update_timestamp_iface_entry;
9797
#define SESSION_FORBIDDEN_CHARS "=,;.[ \t\r\n\013\014"
9898
#define SESSION_FORBIDDEN_CHARS_FOR_ERROR_MSG "=,;.[ \\t\\r\\n\\013\\014"
9999

100+
#define SESSION_FORBIDDEN_COOKIE_CHARS ",; \t\r\n\013\014"
101+
#define SESSION_FORBIDDEN_COOKIE_CHARS_FOR_ERROR_MSG ",; \\t\\r\\n\\013\\014"
102+
100103
#define APPLY_TRANS_SID (PS(use_trans_sid) && !PS(use_only_cookies))
101104

102105
static zend_result php_session_send_cookie(void);
@@ -759,6 +762,18 @@ static PHP_INI_MH(OnUpdateSessionStr)
759762
return OnUpdateStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
760763
}
761764

765+
static PHP_INI_MH(OnUpdateSessionCookieStr)
766+
{
767+
if (new_value && strpbrk(ZSTR_VAL(new_value), SESSION_FORBIDDEN_COOKIE_CHARS) != NULL) {
768+
if (stage != ZEND_INI_STAGE_DEACTIVATE) {
769+
php_error_docref(NULL, E_WARNING, "\"%s\" must not contain any of the following characters \"" SESSION_FORBIDDEN_COOKIE_CHARS_FOR_ERROR_MSG "\"", ZSTR_VAL(entry->name));
770+
}
771+
return FAILURE;
772+
}
773+
774+
return OnUpdateSessionStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
775+
}
776+
762777
static PHP_INI_MH(OnUpdateSessionSameSite)
763778
{
764779
SESSION_CHECK_ACTIVE_STATE;
@@ -946,8 +961,8 @@ PHP_INI_BEGIN()
946961
STD_PHP_INI_ENTRY("session.gc_maxlifetime", "1440", PHP_INI_ALL, OnUpdateSessionLong, gc_maxlifetime, php_ps_globals, ps_globals)
947962
PHP_INI_ENTRY("session.serialize_handler", "php", PHP_INI_ALL, OnUpdateSerializer)
948963
STD_PHP_INI_ENTRY("session.cookie_lifetime", "0", PHP_INI_ALL, OnUpdateCookieLifetime, cookie_lifetime, php_ps_globals, ps_globals)
949-
STD_PHP_INI_ENTRY("session.cookie_path", "/", PHP_INI_ALL, OnUpdateSessionStr, cookie_path, php_ps_globals, ps_globals)
950-
STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateSessionStr, cookie_domain, php_ps_globals, ps_globals)
964+
STD_PHP_INI_ENTRY("session.cookie_path", "/", PHP_INI_ALL, OnUpdateSessionCookieStr, cookie_path, php_ps_globals, ps_globals)
965+
STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateSessionCookieStr, cookie_domain, php_ps_globals, ps_globals)
951966
STD_PHP_INI_BOOLEAN("session.cookie_secure", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_secure, php_ps_globals, ps_globals)
952967
STD_PHP_INI_BOOLEAN("session.cookie_partitioned", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_partitioned, php_ps_globals, ps_globals)
953968
STD_PHP_INI_BOOLEAN("session.cookie_httponly", "1", PHP_INI_ALL, OnUpdateSessionBool, cookie_httponly, php_ps_globals, ps_globals)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
--TEST--
2+
session.cookie_path and session.cookie_domain reject cookie separators
3+
--INI--
4+
session.save_handler=files
5+
session.name=PHPSESSID
6+
session.gc_probability=0
7+
--EXTENSIONS--
8+
session
9+
--FILE--
10+
<?php
11+
12+
ob_start();
13+
14+
var_dump(session_set_cookie_params(['path' => '/; Domain=evil.example']));
15+
var_dump(session_set_cookie_params(['domain' => 'example.com; HttpOnly']));
16+
var_dump(session_set_cookie_params(['path' => "/\r\nX-Injected: yes"]));
17+
var_dump(ini_set('session.cookie_domain', "example.com\tevil"));
18+
19+
$params = session_get_cookie_params();
20+
var_dump($params['path'], $params['domain']);
21+
22+
var_dump(session_set_cookie_params(['path' => '/app', 'domain' => 'example.com']));
23+
$params = session_get_cookie_params();
24+
var_dump($params['path'], $params['domain']);
25+
26+
?>
27+
--EXPECTF--
28+
Warning: session_set_cookie_params(): "session.cookie_path" must not contain any of the following characters ",; \t\r\n\013\014" in %s on line %d
29+
bool(false)
30+
31+
Warning: session_set_cookie_params(): "session.cookie_domain" must not contain any of the following characters ",; \t\r\n\013\014" in %s on line %d
32+
bool(false)
33+
34+
Warning: session_set_cookie_params(): "session.cookie_path" must not contain any of the following characters ",; \t\r\n\013\014" in %s on line %d
35+
bool(false)
36+
37+
Warning: ini_set(): "session.cookie_domain" must not contain any of the following characters ",; \t\r\n\013\014" in %s on line %d
38+
bool(false)
39+
string(1) "/"
40+
string(0) ""
41+
bool(true)
42+
string(4) "/app"
43+
string(11) "example.com"

0 commit comments

Comments
 (0)