php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login

Patch php-7.0.13-setcookie-samesite-attribute for *Network Functions Bug #72230

Patch version 2016-12-01 09:59 UTC

Return to Bug #72230 | Download this patch
Patch Revisions:

Developer: xistence@0x90.nl

--- php-7.0.13/ext/session/php_session.h	2016-11-08 10:07:58.000000000 -0500
+++ php-7.0.13-samesite/ext/session/php_session.h	2016-11-30 22:47:27.391686560 -0500
@@ -159,6 +159,7 @@ typedef struct _php_ps_globals {
 	char *cookie_domain;
 	zend_bool  cookie_secure;
 	zend_bool  cookie_httponly;
+	char *cookie_samesite;
 	ps_module *mod;
 	ps_module *default_mod;
 	void *mod_data;
--- php-7.0.13/ext/session/session.c	2016-11-08 10:07:58.000000000 -0500
+++ php-7.0.13-samesite/ext/session/session.c	2016-12-01 00:43:11.024055357 -0500
@@ -841,6 +841,7 @@ PHP_INI_BEGIN()
 	STD_PHP_INI_ENTRY("session.cookie_domain",      "",          PHP_INI_ALL, OnUpdateString, cookie_domain,      php_ps_globals,    ps_globals)
 	STD_PHP_INI_BOOLEAN("session.cookie_secure",    "",          PHP_INI_ALL, OnUpdateBool,   cookie_secure,      php_ps_globals,    ps_globals)
 	STD_PHP_INI_BOOLEAN("session.cookie_httponly",  "",          PHP_INI_ALL, OnUpdateBool,   cookie_httponly,    php_ps_globals,    ps_globals)
+	STD_PHP_INI_ENTRY("session.cookie_samesite",    "",          PHP_INI_ALL, OnUpdateString, cookie_samesite,    php_ps_globals,    ps_globals)
 	STD_PHP_INI_BOOLEAN("session.use_cookies",      "1",         PHP_INI_ALL, OnUpdateBool,   use_cookies,        php_ps_globals,    ps_globals)
 	STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1",         PHP_INI_ALL, OnUpdateBool,   use_only_cookies,   php_ps_globals,    ps_globals)
 	STD_PHP_INI_BOOLEAN("session.use_strict_mode",  "0",         PHP_INI_ALL, OnUpdateBool,   use_strict_mode,    php_ps_globals,    ps_globals)
@@ -1461,6 +1462,11 @@ static void php_session_send_cookie(void
 		smart_str_appends(&ncookie, COOKIE_HTTPONLY);
 	}
 
+	if (PS(cookie_samesite)[0]) {
+		smart_str_appends(&ncookie, COOKIE_SAMESITE);
+		smart_str_appends(&ncookie, PS(cookie_samesite));
+	}
+
 	smart_str_0(&ncookie);
 
 	php_session_remove_cookie(); /* remove already sent session ID cookie */
@@ -1733,18 +1739,18 @@ PHPAPI void session_adapt_url(const char
    * Userspace exported functions *
    ******************************** */
 
-/* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]])
+/* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]])
    Set session cookie parameters */
 static PHP_FUNCTION(session_set_cookie_params)
 {
 	zval *lifetime;
-	zend_string *path = NULL, *domain = NULL;
+	zend_string *path = NULL, *domain = NULL, *samesite = NULL;
 	int argc = ZEND_NUM_ARGS();
 	zend_bool secure = 0, httponly = 0;
 	zend_string *ini_name;
 
 	if (!PS(use_cookies) ||
-		zend_parse_parameters(argc, "z|SSbb", &lifetime, &path, &domain, &secure, &httponly) == FAILURE) {
+		zend_parse_parameters(argc, "z|SSbbS", &lifetime, &path, &domain, &secure, &httponly, &samesite) == FAILURE) {
 		return;
 	}
 
@@ -1775,6 +1781,12 @@ static PHP_FUNCTION(session_set_cookie_p
 		zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
 		zend_string_release(ini_name);
 	}
+
+        if (argc > 5) {
+                ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0);
+                zend_alter_ini_entry(ini_name, samesite, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+                zend_string_release(ini_name);
+        }
 }
 /* }}} */
 
@@ -1793,6 +1805,7 @@ static PHP_FUNCTION(session_get_cookie_p
 	add_assoc_string(return_value, "domain", PS(cookie_domain));
 	add_assoc_bool(return_value, "secure", PS(cookie_secure));
 	add_assoc_bool(return_value, "httponly", PS(cookie_httponly));
+	add_assoc_string(return_value, "samesite", PS(cookie_samesite));
 }
 /* }}} */
 
@@ -2516,6 +2529,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_s
 	ZEND_ARG_INFO(0, domain)
 	ZEND_ARG_INFO(0, secure)
 	ZEND_ARG_INFO(0, httponly)
+	ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO(arginfo_session_class_open, 0)
--- php-7.0.13/ext/standard/head.c	2016-11-08 10:08:26.000000000 -0500
+++ php-7.0.13-samesite/ext/standard/head.c	2016-12-01 02:05:43.174605187 -0500
@@ -77,7 +77,7 @@ PHPAPI int php_header(void)
 }
 
 
-PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly)
+PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly, zend_string *samesite)
 {
 	char *cookie;
 	size_t len = sizeof("Set-Cookie: ");
@@ -117,6 +117,9 @@ PHPAPI int php_setcookie(zend_string *na
 	if (domain) {
 		len += ZSTR_LEN(domain);
 	}
+	if (samesite) {
+		len += ZSTR_LEN(domain);
+	}
 
 	cookie = emalloc(len + 100);
 
@@ -172,6 +175,10 @@ PHPAPI int php_setcookie(zend_string *na
 	if (httponly) {
 		strlcat(cookie, COOKIE_HTTPONLY, len + 100);
 	}
+	if (samesite && ZSTR_LEN(samesite)) {
+		strlcat(cookie, COOKIE_SAMESITE, len + 100);
+		strlcat(cookie, ZSTR_VAL(samesite), len + 100);
+	}
 
 	ctr.line = cookie;
 	ctr.line_len = (uint)strlen(cookie);
@@ -183,20 +190,20 @@ PHPAPI int php_setcookie(zend_string *na
 
 
 /* php_set_cookie(name, value, expires, path, domain, secure) */
-/* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
+/* {{{ proto bool setcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
    Send a cookie */
 PHP_FUNCTION(setcookie)
 {
-	zend_string *name, *value = NULL, *path = NULL, *domain = NULL;
+	zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
 	zend_long expires = 0;
 	zend_bool secure = 0, httponly = 0;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SlSSbb",
-				&name, &value, &expires, &path, &domain, &secure, &httponly) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SlSSbbS",
+				&name, &value, &expires, &path, &domain, &secure, &httponly, &samesite) == FAILURE) {
 		return;
 	}
 
-	if (php_setcookie(name, value, expires, path, domain, secure, 1, httponly) == SUCCESS) {
+	if (php_setcookie(name, value, expires, path, domain, secure, 1, httponly, samesite) == SUCCESS) {
 		RETVAL_TRUE;
 	} else {
 		RETVAL_FALSE;
@@ -204,20 +211,20 @@ PHP_FUNCTION(setcookie)
 }
 /* }}} */
 
-/* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly]]]]]])
+/* {{{ proto bool setrawcookie(string name [, string value [, int expires [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]]]])
    Send a cookie with no url encoding of the value */
 PHP_FUNCTION(setrawcookie)
 {
-	zend_string *name, *value = NULL, *path = NULL, *domain = NULL;
+	zend_string *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL;
 	zend_long expires = 0;
 	zend_bool secure = 0, httponly = 0;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SlSSbb",
-				&name, &value, &expires, &path, &domain, &secure, &httponly) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SlSSbbS",
+				&name, &value, &expires, &path, &domain, &secure, &httponly, &samesite) == FAILURE) {
 		return;
 	}
 
-	if (php_setcookie(name, value, expires, path, domain, secure, 0, httponly) == SUCCESS) {
+	if (php_setcookie(name, value, expires, path, domain, secure, 0, httponly, samesite) == SUCCESS) {
 		RETVAL_TRUE;
 	} else {
 		RETVAL_FALSE;
--- php-7.0.13/ext/standard/head.h	2016-11-08 10:08:26.000000000 -0500
+++ php-7.0.13-samesite/ext/standard/head.h	2016-11-30 23:28:24.927463160 -0500
@@ -27,6 +27,7 @@
 #define COOKIE_PATH       "; path="
 #define COOKIE_SECURE     "; secure"
 #define COOKIE_HTTPONLY   "; HttpOnly"
+#define COOKIE_SAMESITE   "; SameSite="
 
 extern PHP_RINIT_FUNCTION(head);
 PHP_FUNCTION(header);
@@ -38,6 +39,6 @@ PHP_FUNCTION(headers_list);
 PHP_FUNCTION(http_response_code);
 
 PHPAPI int php_header(void);
-PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly);
+PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int url_encode, int httponly, zend_string *samesite);
 
 #endif
--- php-7.0.13/ext/standard/basic_functions.c	2016-11-08 10:08:26.000000000 -0500
+++ php-7.0.13-samesite/ext/standard/basic_functions.c	2016-11-30 23:29:23.067457875 -0500
@@ -1426,6 +1426,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie
 	ZEND_ARG_INFO(0, domain)
 	ZEND_ARG_INFO(0, secure)
 	ZEND_ARG_INFO(0, httponly)
+	ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_setrawcookie, 0, 0, 1)
@@ -1436,6 +1437,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setrawcoo
 	ZEND_ARG_INFO(0, domain)
 	ZEND_ARG_INFO(0, secure)
 	ZEND_ARG_INFO(0, httponly)
+	ZEND_ARG_INFO(0, samesite)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_headers_sent, 0, 0, 0)
--- php-7.0.13/ext/session/tests/session_get_cookie_params_variation1.phpt	2016-11-08 10:07:57.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_get_cookie_params_variation1.phpt	2016-11-30 23:35:43.963423250 -0500
@@ -8,6 +8,7 @@ session.cookie_path="/"
 session.cookie_domain=""
 session.cookie_secure=0
 session.cookie_httponly=0
+session.cookie_samesite=""
 --FILE--
 <?php
 
@@ -32,13 +33,15 @@ ini_set("session.cookie_secure", TRUE);
 var_dump(session_get_cookie_params());
 ini_set("session.cookie_httponly", TRUE);
 var_dump(session_get_cookie_params());
+ini_set("session.cookie_samesite", "foo");
+var_dump(session_get_cookie_params());
 
 echo "Done";
 ob_end_flush();
 ?>
 --EXPECTF--
 *** Testing session_get_cookie_params() : variation ***
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(0)
   ["path"]=>
@@ -49,8 +52,10 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -61,8 +66,10 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -73,8 +80,10 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -85,8 +94,10 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -97,8 +108,10 @@ array(5) {
   bool(true)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -109,6 +122,22 @@ array(5) {
   bool(true)
   ["httponly"]=>
   bool(true)
+  ["samesite"]=>
+  string(0) ""
+}
+array(6) {
+  ["lifetime"]=>
+  int(3600)
+  ["path"]=>
+  string(5) "/path"
+  ["domain"]=>
+  string(3) "foo"
+  ["secure"]=>
+  bool(true)
+  ["httponly"]=>
+  bool(true)
+  ["samesite"]=>
+  string(3) "foo"
 }
 Done
 
--- php-7.0.13/ext/session/tests/session_set_cookie_params_variation2.phpt	2016-11-08 10:07:57.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_set_cookie_params_variation2.phpt	2016-12-01 03:23:14.475182366 -0500
@@ -10,7 +10,7 @@ session.cookie_path=/path
 ob_start();
 
 /* 
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly [, string $samesite]]]]])
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c 
  */
--- php-7.0.13/ext/session/tests/session_get_cookie_params_basic.phpt	2016-11-08 10:07:58.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_get_cookie_params_basic.phpt	2016-11-30 23:39:44.106401420 -0500
@@ -8,6 +8,7 @@ session.cookie_path="/"
 session.cookie_domain=""
 session.cookie_secure=0
 session.cookie_httponly=0
+session.cookie_samesite=""
 --FILE--
 <?php
 
@@ -22,9 +23,9 @@ ob_start();
 echo "*** Testing session_get_cookie_params() : basic functionality ***\n";
 
 var_dump(session_get_cookie_params());
-var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE));
+var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "foo"));
 var_dump(session_get_cookie_params());
-var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE));
+var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE, "blah"));
 var_dump(session_get_cookie_params());
 
 echo "Done";
@@ -32,7 +33,7 @@ ob_end_flush();
 ?>
 --EXPECTF--
 *** Testing session_get_cookie_params() : basic functionality ***
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(0)
   ["path"]=>
@@ -43,9 +44,11 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(0) ""
 }
 NULL
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(3600)
   ["path"]=>
@@ -56,9 +59,11 @@ array(5) {
   bool(false)
   ["httponly"]=>
   bool(false)
+  ["samesite"]=>
+  string(3) "foo"
 }
 NULL
-array(5) {
+array(6) {
   ["lifetime"]=>
   int(1234567890)
   ["path"]=>
@@ -69,5 +74,7 @@ array(5) {
   bool(true)
   ["httponly"]=>
   bool(true)
+  ["samesite"]=>
+  string(4) "blah"
 }
 Done
--- php-7.0.13/ext/session/tests/session_set_cookie_params_variation3.phpt	2016-11-08 10:07:58.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_set_cookie_params_variation3.phpt	2016-11-30 23:41:34.784391359 -0500
@@ -10,7 +10,7 @@ session.cookie_domain=foo
 ob_start();
 
 /* 
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly[, string $samesite]]]]])
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c 
  */
--- php-7.0.13/ext/session/tests/session_set_cookie_params_variation5.phpt	2016-11-08 10:07:58.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_set_cookie_params_variation5.phpt	2016-11-30 23:42:57.675383824 -0500
@@ -10,7 +10,7 @@ session.cookie_httponly=TRUE
 ob_start();
 
 /* 
- * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly[, string $samesite]]]]])
  * Description : Set the session cookie parameters
  * Source code : ext/session/session.c 
  */
--- php-7.0.13/ext/session/tests/session_set_cookie_params_variation6.phpt	1969-12-31 19:00:00.000000000 -0500
+++ php-7.0.13-samesite/ext/session/tests/session_set_cookie_params_variation6.phpt	2016-11-30 23:49:31.034348066 -0500
@@ -0,0 +1,49 @@
+--TEST--
+Test session_set_cookie_params() function : variation
+--INI--
+session.cookie_samesite=test
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/* 
+ * Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $samesite[, string $samesite]]]]])
+ * Description : Set the session cookie parameters
+ * Source code : ext/session/session.c 
+ */
+
+echo "*** Testing session_set_cookie_params() : variation ***\n";
+
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "nothing"));
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_start());
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, TRUE, "test"));
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_destroy());
+var_dump(ini_get("session.cookie_samesite"));
+var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE, "nothing"));
+var_dump(ini_get("session.cookie_samesite"));
+
+echo "Done";
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_set_cookie_params() : variation ***
+string(4) "test"
+NULL
+string(7) "nothing"
+bool(true)
+string(7) "nothing"
+NULL
+string(4) "test"
+bool(true)
+string(4) "test"
+NULL
+string(7) "nothing"
+Done
+
--- php-7.0.13/php.ini-development	2016-11-08 10:07:39.000000000 -0500
+++ php-7.0.13-samesite/php.ini-development	2016-12-01 03:41:03.156085218 -0500
@@ -1363,6 +1363,11 @@ session.cookie_domain =
 ; http://php.net/session.cookie-httponly
 session.cookie_httponly =
 
+; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF)
+; Current valid values are "Lax" or "Strict"
+; https://tools.ietf.org/html/draft-west-first-party-cookies-07
+session.cookie_samesite =
+
 ; Handler used to serialize data.  php is the standard serializer of PHP.
 ; http://php.net/session.serialize-handler
 session.serialize_handler = php
--- php-7.0.13/php.ini-production	2016-11-08 10:07:39.000000000 -0500
+++ php-7.0.13-samesite/php.ini-production	2016-12-01 03:41:20.873083608 -0500
@@ -1363,6 +1363,11 @@ session.cookie_domain =
 ; http://php.net/session.cookie-httponly
 session.cookie_httponly =
 
+; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF)
+; Current valid values are "Lax" or "Strict"
+; https://tools.ietf.org/html/draft-west-first-party-cookies-07
+session.cookie_samesite =
+
 ; Handler used to serialize data.  php is the standard serializer of PHP.
 ; http://php.net/session.serialize-handler
 session.serialize_handler = php
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 11:01:29 2024 UTC