Commit 48d588d2530 for php.net
commit 48d588d2530163071db9f0a01c5aa44a23dd6723
Author: Jorg Adam Sowa <jorg.sowa@gmail.com>
Date: Mon May 18 13:50:52 2026 +0200
ext/session: reject null bytes in session.cookie_path, session.cookie_domain, session.cache_limiter (#22074)
Reject null bytes in `session.cookie_path`, `session.cookie_domain`, `session.cache_limiter`.
- `cookie_path` / `cookie_domain`: When the session cookie was sent, `php_session_send_cookie()` built the Set-Cookie header including the raw null byte. The SAPI layer (sapi_header_op) emitted a generic `E_WARNING: Header may not contain NUL bytes` and returned FAILURE, but `php_session_send_cookie()` did not check that return value and returned SUCCESS — so the session cookie was silently dropped while `session_start()` still returned true.
- `cache_limiter`: The limiter name is looked up via `strcasecmp(lim->name, ZSTR_VAL(PS(cache_limiter)))`, which stops at the null byte, so "nocache\0evil" would silently behave as "nocache". The null byte was effectively ignored but still accepted without complaint.
diff --git a/NEWS b/NEWS
index f86892d0212..239becd194f 100644
--- a/NEWS
+++ b/NEWS
@@ -154,6 +154,8 @@ PHP NEWS
- Session:
. Fixed bug 71162 (updateTimestamp never called when session data is empty).
(Girgias)
+ . Null bytes in session.cookie_path, session.cookie_domain, and
+ session.cache_limiter are now rejected with a warning. (jorgsowa)
- Soap:
. Soap::__setCookie() when cookie name is a digit is now not stored and
diff --git a/UPGRADING b/UPGRADING
index 5aa617b99fd..1bba53274c1 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -75,6 +75,11 @@ PHP 8.6 UPGRADE NOTES
argument value is passed.
- Session:
+ . Setting session.cookie_path, session.cookie_domain, or session.cache_limiter
+ to a value containing null bytes now emits a warning and leaves the setting
+ unchanged. Previously, null bytes were silently accepted: for cookie_path and
+ cookie_domain this caused the SAPI to drop the Set-Cookie header; for
+ cache_limiter the value was silently truncated at the null byte.
. A ValueError is not thrown if $name is a string containing null bytes in
session_module_name().
. session_encode() now returns an empty string instead of false for empty
diff --git a/ext/session/session.c b/ext/session/session.c
index 3985925ca2b..aa6cfb311af 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -734,6 +734,13 @@ static PHP_INI_MH(OnUpdateSessionStr)
SESSION_CHECK_ACTIVE_STATE;
SESSION_CHECK_OUTPUT_STATE;
+ if (new_value && zend_str_has_nul_byte(new_value)) {
+ if (stage != ZEND_INI_STAGE_DEACTIVATE) {
+ php_error_docref(NULL, E_WARNING, "\"%s\" must not contain null bytes", ZSTR_VAL(entry->name));
+ }
+ return FAILURE;
+ }
+
return OnUpdateStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}
diff --git a/ext/session/tests/session_str_settings_null_byte.phpt b/ext/session/tests/session_str_settings_null_byte.phpt
new file mode 100644
index 00000000000..693ec697160
--- /dev/null
+++ b/ext/session/tests/session_str_settings_null_byte.phpt
@@ -0,0 +1,37 @@
+--TEST--
+session.cookie_path, session.cookie_domain, and session.cache_limiter must not contain null bytes
+--EXTENSIONS--
+session
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+var_dump(ini_set('session.cookie_path', "/path\0evil"));
+var_dump(ini_set('session.cookie_domain', "example\0evil.com"));
+var_dump(ini_set('session.cache_limiter', "nocache\0evil"));
+
+var_dump(session_set_cookie_params(0, "/path\0evil"));
+var_dump(session_set_cookie_params(0, null, "example\0evil.com"));
+
+ob_end_flush();
+echo "Done";
+?>
+--EXPECTF--
+Warning: ini_set(): "session.cookie_path" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: ini_set(): "session.cookie_domain" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: ini_set(): "session.cache_limiter" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: session_set_cookie_params(): "session.cookie_path" must not contain null bytes in %s on line %d
+bool(false)
+
+Warning: session_set_cookie_params(): "session.cookie_domain" must not contain null bytes in %s on line %d
+bool(false)
+Done