-
Notifications
You must be signed in to change notification settings - Fork 7.9k
[RFC] SameSite cookie option #3398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -804,6 +804,7 @@ PHP_INI_BEGIN() | |
STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateSessionString, cookie_domain, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.cookie_secure", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_secure, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("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, OnUpdateString, cookie_samesite, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.use_cookies", "1", PHP_INI_ALL, OnUpdateSessionBool, use_cookies, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateSessionBool, use_only_cookies, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.use_strict_mode", "0", PHP_INI_ALL, OnUpdateSessionBool, use_strict_mode, php_ps_globals, ps_globals) | ||
|
@@ -1362,6 +1363,11 @@ static int 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 */ | ||
|
@@ -1661,20 +1667,30 @@ PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t | |
******************************** */ | ||
|
||
/* {{{ proto bool session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly]]]]) | ||
session_set_cookie_params(array options) | ||
Set session cookie parameters */ | ||
static PHP_FUNCTION(session_set_cookie_params) | ||
{ | ||
zval *lifetime; | ||
zend_string *path = NULL, *domain = NULL; | ||
int argc = ZEND_NUM_ARGS(); | ||
zend_bool secure = 0, httponly = 0; | ||
zval *lifetime_or_options = NULL; | ||
zend_string *lifetime = NULL, *path = NULL, *domain = NULL, *samesite = NULL; | ||
zend_bool secure = 0, secure_null = 1; | ||
zend_bool httponly = 0, httponly_null = 1; | ||
zend_string *ini_name; | ||
int result; | ||
int found = 0; | ||
|
||
if (!PS(use_cookies) || | ||
zend_parse_parameters(argc, "z|SSbb", &lifetime, &path, &domain, &secure, &httponly) == FAILURE) { | ||
if (!PS(use_cookies)) { | ||
return; | ||
} | ||
|
||
ZEND_PARSE_PARAMETERS_START(1, 5) | ||
Z_PARAM_ZVAL(lifetime_or_options) | ||
Z_PARAM_OPTIONAL | ||
Z_PARAM_STR(path) | ||
Z_PARAM_STR(domain) | ||
Z_PARAM_BOOL_EX(secure, secure_null, 1, 0) | ||
Z_PARAM_BOOL_EX(httponly, httponly_null, 1, 0) | ||
ZEND_PARSE_PARAMETERS_END(); | ||
|
||
if (PS(session_status) == php_session_active) { | ||
php_error_docref(NULL, E_WARNING, "Cannot change session cookie parameters when session is active"); | ||
|
@@ -1686,47 +1702,106 @@ static PHP_FUNCTION(session_set_cookie_params) | |
RETURN_FALSE; | ||
} | ||
|
||
convert_to_string_ex(lifetime); | ||
if (Z_TYPE_P(lifetime_or_options) == IS_ARRAY) { | ||
zend_string *key; | ||
zval *value; | ||
|
||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(lifetime_or_options), key, value) { | ||
if (key) { | ||
ZVAL_DEREF(value); | ||
if(!strcasecmp("lifetime", ZSTR_VAL(key))) { | ||
lifetime = zval_get_string(value); | ||
found++; | ||
} else if(!strcasecmp("path", ZSTR_VAL(key))) { | ||
path = zval_get_string(value); | ||
found++; | ||
} else if(!strcasecmp("domain", ZSTR_VAL(key))) { | ||
domain = zval_get_string(value); | ||
found++; | ||
} else if(!strcasecmp("secure", ZSTR_VAL(key))) { | ||
secure = zval_is_true(value); | ||
secure_null = 0; | ||
found++; | ||
} else if(!strcasecmp("httponly", ZSTR_VAL(key))) { | ||
httponly = zval_is_true(value); | ||
httponly_null = 0; | ||
found++; | ||
} else if(!strcasecmp("samesite", ZSTR_VAL(key))) { | ||
samesite = zval_get_string(value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we want to check that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could, but that falls a bit out of the scope here. No other values are sanitized at this moment. It would probably be a good proposal to validate all values (ie. check if the domain is actually a domain) but I'd keep that a separate discussion. |
||
found++; | ||
} else { | ||
php_error_docref(NULL, E_WARNING, "Unrecognized key '%s' found in the options array", ZSTR_VAL(key)); | ||
} | ||
} else { | ||
php_error_docref(NULL, E_WARNING, "Numeric key found in the options array"); | ||
} | ||
} ZEND_HASH_FOREACH_END(); | ||
|
||
ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0); | ||
if (zend_alter_ini_entry(ini_name, Z_STR_P(lifetime), PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { | ||
zend_string_release_ex(ini_name, 0); | ||
RETURN_FALSE; | ||
if (found == 0) { | ||
php_error_docref(NULL, E_WARNING, "No valid keys were found in the options array"); | ||
RETURN_FALSE; | ||
} | ||
} else { | ||
lifetime = zval_get_string(lifetime_or_options); | ||
} | ||
zend_string_release_ex(ini_name, 0); | ||
|
||
if (lifetime) { | ||
ini_name = zend_string_init("session.cookie_lifetime", sizeof("session.cookie_lifetime") - 1, 0); | ||
result = zend_alter_ini_entry(ini_name, lifetime, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
zend_string_release(lifetime); | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
} | ||
if (path) { | ||
ini_name = zend_string_init("session.cookie_path", sizeof("session.cookie_path") - 1, 0); | ||
if (zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { | ||
zend_string_release_ex(ini_name, 0); | ||
RETURN_FALSE; | ||
result = zend_alter_ini_entry(ini_name, path, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
if (found > 0) { | ||
zend_string_release(path); | ||
} | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
} | ||
if (domain) { | ||
ini_name = zend_string_init("session.cookie_domain", sizeof("session.cookie_domain") - 1, 0); | ||
if (zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { | ||
zend_string_release_ex(ini_name, 0); | ||
RETURN_FALSE; | ||
result = zend_alter_ini_entry(ini_name, domain, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
if (found > 0) { | ||
zend_string_release(domain); | ||
} | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
} | ||
|
||
if (argc > 3) { | ||
if (!secure_null) { | ||
ini_name = zend_string_init("session.cookie_secure", sizeof("session.cookie_secure") - 1, 0); | ||
if (zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { | ||
zend_string_release_ex(ini_name, 0); | ||
result = zend_alter_ini_entry_chars(ini_name, secure ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
zend_string_release_ex(ini_name, 0); | ||
} | ||
if (argc > 4) { | ||
if (!httponly_null) { | ||
ini_name = zend_string_init("session.cookie_httponly", sizeof("session.cookie_httponly") - 1, 0); | ||
if (zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { | ||
zend_string_release_ex(ini_name, 0); | ||
result = zend_alter_ini_entry_chars(ini_name, httponly ? "1" : "0", 1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
} | ||
if (samesite) { | ||
ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0); | ||
result = zend_alter_ini_entry(ini_name, samesite, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); | ||
if (found > 0) { | ||
zend_string_release(samesite); | ||
} | ||
zend_string_release_ex(ini_name, 0); | ||
if (result == FAILURE) { | ||
RETURN_FALSE; | ||
} | ||
} | ||
|
||
RETURN_TRUE; | ||
|
@@ -1748,6 +1823,7 @@ static PHP_FUNCTION(session_get_cookie_params) | |
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)); | ||
} | ||
/* }}} */ | ||
|
||
|
@@ -2627,7 +2703,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_expire, 0, 0, 0) | |
ZEND_END_ARG_INFO() | ||
|
||
ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_cookie_params, 0, 0, 1) | ||
ZEND_ARG_INFO(0, lifetime) | ||
ZEND_ARG_INFO(0, lifetime_or_options) | ||
ZEND_ARG_INFO(0, path) | ||
ZEND_ARG_INFO(0, domain) | ||
ZEND_ARG_INFO(0, secure) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to add
SameSite
here too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, samesite will only be supported in the new array forms of these functions, not the (now legacy) multi-argument variant.