diff --git a/NEWS b/NEWS index 1200d086067c..e37effff3afe 100644 --- a/NEWS +++ b/NEWS @@ -156,6 +156,8 @@ PHP NEWS (Girgias) . Null bytes in session.cookie_path, session.cookie_domain, and session.cache_limiter are now rejected with a warning. (jorgsowa) + . Changed defaults of session.use_strict_mode (now 1), session.cookie_httponly + (now 1) and session.cookie_samesite (now "Lax"). (jorgsowa) - Soap: . Soap::__setCookie() when cookie name is a digit is now not stored and diff --git a/UPGRADING b/UPGRADING index 1bba53274c16..a3b22d8c0560 100644 --- a/UPGRADING +++ b/UPGRADING @@ -94,6 +94,24 @@ PHP 8.6 UPGRADE NOTES comparison. Custom session handlers that rely on write() being called with empty data (e.g. to destroy the session) should implement the same logic in their updateTimestamp() method. + . The defaults of three session INI settings have changed to provide secure + behavior out of the box: + - session.use_strict_mode is now 1 (was 0). Strict mode rejects + uninitialized session IDs, mitigating session fixation. Custom session + handlers that previously relied on accepting externally supplied IDs + without a corresponding storage entry must either implement + validateId() / create_sid() or explicitly set this to 0. + - session.cookie_httponly is now 1 (was 0). Session cookies are no + longer accessible to JavaScript via document.cookie. Applications + that read the session cookie from JavaScript must explicitly set + this to 0. + - session.cookie_samesite is now "Lax" (was unset). Session cookies + are no longer sent on cross-site requests other than top-level + navigations using safe HTTP methods. Applications that depend on + session cookies being sent on cross-site POST submissions must + explicitly set this to "None" (and also set session.cookie_secure + to 1). + RFC: https://wiki.php.net/rfc/session_security_defaults - SPL: . SplObjectStorage::getHash() implementations may no longer mutate any diff --git a/ext/session/session.c b/ext/session/session.c index aa6cfb311afa..1fdfc5d1073f 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -928,11 +928,11 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateSessionStr, cookie_domain, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.cookie_secure", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_secure, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.cookie_partitioned", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_partitioned, php_ps_globals, ps_globals) - STD_PHP_INI_BOOLEAN("session.cookie_httponly", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_httponly, php_ps_globals, ps_globals) - STD_PHP_INI_ENTRY("session.cookie_samesite", "", PHP_INI_ALL, OnUpdateSessionSameSite, cookie_samesite, php_ps_globals, ps_globals) + STD_PHP_INI_BOOLEAN("session.cookie_httponly", "1", PHP_INI_ALL, OnUpdateSessionBool, cookie_httponly, php_ps_globals, ps_globals) + STD_PHP_INI_ENTRY("session.cookie_samesite", "Lax", PHP_INI_ALL, OnUpdateSessionSameSite, cookie_samesite, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_cookies", "1", PHP_INI_ALL, OnUpdateSessionBool, use_cookies, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateUseOnlyCookies, use_only_cookies, php_ps_globals, ps_globals) - STD_PHP_INI_BOOLEAN("session.use_strict_mode", "0", PHP_INI_ALL, OnUpdateSessionBool, use_strict_mode, php_ps_globals, ps_globals) + STD_PHP_INI_BOOLEAN("session.use_strict_mode", "1", PHP_INI_ALL, OnUpdateSessionBool, use_strict_mode, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateRefererCheck, extern_referer_chk, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateSessionStr, cache_limiter, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateSessionLong, cache_expire, php_ps_globals, ps_globals) diff --git a/ext/session/tests/bug74892.phpt b/ext/session/tests/bug74892.phpt index 916120cb4d44..7a2211210b45 100644 --- a/ext/session/tests/bug74892.phpt +++ b/ext/session/tests/bug74892.phpt @@ -4,6 +4,7 @@ Bug #74892 Url Rewriting (trans_sid) not working on urls that start with # session.use_cookies=0 session.use_only_cookies=0 session.use_trans_sid=1 +session.use_strict_mode=0 --EXTENSIONS-- session --SKIPIF-- diff --git a/ext/session/tests/bug80774.phpt b/ext/session/tests/bug80774.phpt index 7dd86add02b0..ba5d39e1953a 100644 --- a/ext/session/tests/bug80774.phpt +++ b/ext/session/tests/bug80774.phpt @@ -2,6 +2,8 @@ Bug #80774 (session_name() problem with backslash) --EXTENSIONS-- session +--INI-- +session.use_strict_mode=0 --FILE-- --EXPECTHEADERS-- -Set-Cookie: foo\bar=12345; path=/ +Set-Cookie: foo\bar=12345; path=/; HttpOnly; SameSite=Lax --EXPECT-- diff --git a/ext/session/tests/gh9200.phpt b/ext/session/tests/gh9200.phpt index fd6e28ab8b90..7e458176d10f 100644 --- a/ext/session/tests/gh9200.phpt +++ b/ext/session/tests/gh9200.phpt @@ -2,6 +2,7 @@ GH-9200: setcookie has an obsolete expires date format --INI-- session.cookie_lifetime=3600 +session.use_strict_mode=0 --EXTENSIONS-- session --CGI-- @@ -12,7 +13,7 @@ session_id('bar'); session_start(); foreach (headers_list() as $header) { - if (preg_match('/^Set-Cookie: foo=bar; expires=(Mon|Tue|Wed|Thu|Fri|Sat|Sun), [0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2[0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT; Max-Age=3600; path=\\/$/', $header)) { + if (preg_match('/^Set-Cookie: foo=bar; expires=(Mon|Tue|Wed|Thu|Fri|Sat|Sun), [0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2[0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT; Max-Age=3600; path=\/; HttpOnly; SameSite=Lax$/', $header)) { echo "Success", PHP_EOL; exit; } diff --git a/ext/session/tests/mod_files/gc_dirdepth2.phpt b/ext/session/tests/mod_files/gc_dirdepth2.phpt index a8724285125a..d858c1f95db8 100644 --- a/ext/session/tests/mod_files/gc_dirdepth2.phpt +++ b/ext/session/tests/mod_files/gc_dirdepth2.phpt @@ -7,6 +7,7 @@ session --INI-- session.gc_probability=0 session.gc_maxlifetime=10 +session.use_strict_mode=0 --FILE-- --EXPECTF-- Warning: PHP Startup: session.cookie_samesite must be "Strict", "Lax", "None", or "" in Unknown on line 0 -string(0) "" +string(3) "Lax" Done diff --git a/ext/session/tests/session_start_partitioned_headers.phpt b/ext/session/tests/session_start_partitioned_headers.phpt index 6fa3815aa85d..4a2c7dcf878f 100644 --- a/ext/session/tests/session_start_partitioned_headers.phpt +++ b/ext/session/tests/session_start_partitioned_headers.phpt @@ -2,6 +2,8 @@ session_start() with partitioned cookies - header test --EXTENSIONS-- session +--INI-- +session.use_strict_mode=0 --FILE-- true, "partitioned" => true]); session_start(); ?> --EXPECTHEADERS-- -Set-Cookie: PHPSESSID=12345; path=/; secure; Partitioned +Set-Cookie: PHPSESSID=12345; path=/; secure; Partitioned; HttpOnly; SameSite=Lax --EXPECT-- diff --git a/php.ini-development b/php.ini-development index ee75459ea56c..78ae50708d5a 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1305,10 +1305,9 @@ session.save_handler = files ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption -; vulnerability. It is disabled by default for maximum compatibility, but -; enabling it is encouraged. +; vulnerability. ; https://wiki.php.net/rfc/strict_sessions -session.use_strict_mode = 0 +session.use_strict_mode = 1 ; Whether to use cookies. ; https://php.net/session.use-cookies @@ -1350,13 +1349,13 @@ session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly -session.cookie_httponly = +session.cookie_httponly = 1 ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 -session.cookie_samesite = +session.cookie_samesite = "Lax" ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler diff --git a/php.ini-production b/php.ini-production index b10e2ba9944a..eb6880fe75d6 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1307,10 +1307,9 @@ session.save_handler = files ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption -; vulnerability. It is disabled by default for maximum compatibility, but -; enabling it is encouraged. +; vulnerability. ; https://wiki.php.net/rfc/strict_sessions -session.use_strict_mode = 0 +session.use_strict_mode = 1 ; Whether to use cookies. ; https://php.net/session.use-cookies @@ -1352,13 +1351,13 @@ session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly -session.cookie_httponly = +session.cookie_httponly = 1 ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 -session.cookie_samesite = +session.cookie_samesite = "Lax" ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler