Skip to content

Commit

Permalink
Support for samesite cookies with array syntax
Browse files Browse the repository at this point in the history
Allows using an alternative array argument with
support for the samesite option on the following
functions:
setcookie
setrawcookie
session_set_cookie_params
  • Loading branch information
pmmaga authored and cmb69 committed Jul 31, 2018
1 parent 08b9310 commit 2b58ab2
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 65 deletions.
132 changes: 97 additions & 35 deletions ext/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1664,21 +1664,31 @@ PHPAPI void session_adapt_url(const char *url, size_t urllen, char **new, size_t
* Userspace exported functions *
******************************** */

/* {{{ proto void session_set_cookie_params(int lifetime [, string path [, string domain [, bool secure[, bool httponly[, string samesite]]]]])
/* {{{ 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, *samesite = 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|SSbbS", &lifetime, &path, &domain, &secure, &httponly, &samesite) == 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");
Expand All @@ -1690,55 +1700,108 @@ 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);
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;
}
}

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);
}

RETURN_TRUE;
}
/* }}} */
Expand Down Expand Up @@ -2638,12 +2701,11 @@ 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)
ZEND_ARG_INFO(0, httponly)
ZEND_ARG_INFO(0, samesite)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_session_class_open, 0)
Expand Down
31 changes: 27 additions & 4 deletions ext/session/tests/session_get_cookie_params_basic.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,17 @@ 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, "foo"));
var_dump(session_set_cookie_params(3600, "/path", "blah", FALSE, FALSE));
var_dump(session_get_cookie_params());
var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE, "blah"));
var_dump(session_set_cookie_params(1234567890, "/guff", "foo", TRUE, TRUE));
var_dump(session_get_cookie_params());
var_dump(session_set_cookie_params([
"lifetime" => 123,
"path" => "/bar",
"domain" => "baz",
"secure" => FALSE,
"httponly" => FALSE,
"samesite" => "please"]));
var_dump(session_get_cookie_params());

echo "Done";
Expand Down Expand Up @@ -60,7 +68,7 @@ array(6) {
["httponly"]=>
bool(false)
["samesite"]=>
string(3) "foo"
string(0) ""
}
bool(true)
array(6) {
Expand All @@ -75,6 +83,21 @@ array(6) {
["httponly"]=>
bool(true)
["samesite"]=>
string(4) "blah"
string(0) ""
}
bool(true)
array(6) {
["lifetime"]=>
int(123)
["path"]=>
string(4) "/bar"
["domain"]=>
string(3) "baz"
["secure"]=>
bool(false)
["httponly"]=>
bool(false)
["samesite"]=>
string(6) "please"
}
Done
Original file line number Diff line number Diff line change
Expand Up @@ -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[, string $samesite]]]]])
* Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
* Description : Set the session cookie parameters
* Source code : ext/session/session.c
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[, string $samesite]]]]])
* Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $httponly]]]])
* Description : Set the session cookie parameters
* Source code : ext/session/session.c
*/
Expand Down
8 changes: 4 additions & 4 deletions ext/session/tests/session_set_cookie_params_variation6.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ session.cookie_samesite=test
ob_start();

/*
* Prototype : void session_set_cookie_params(int $lifetime [, string $path [, string $domain [, bool $secure [, bool $samesite[, string $samesite]]]]])
* Prototype : void session_set_cookie_params(array $options)
* 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(session_set_cookie_params(["samesite" => "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(session_set_cookie_params(["samesite" => "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, "other"));
var_dump(session_set_cookie_params(["samesite" => "other"]));
var_dump(ini_get("session.cookie_samesite"));

echo "Done";
Expand Down
60 changes: 60 additions & 0 deletions ext/session/tests/session_set_cookie_params_variation7.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
Test session_set_cookie_params() function : array parameter variation
--INI--
session.cookie_lifetime=0
session.cookie_path="/"
session.cookie_domain=""
session.cookie_secure=0
session.cookie_httponly=0
session.cookie_samesite=""
--SKIPIF--
<?php include('skipif.inc'); ?>
--FILE--
<?php

ob_start();

/*
* Prototype : void session_set_cookie_params(array $options)
* Description : Set the session cookie parameters
* Source code : ext/session/session.c
*/

echo "*** Testing session_set_cookie_params() : array parameter variation ***\n";

// Invalid cases
var_dump(session_set_cookie_params([]));
var_dump(session_set_cookie_params(["unknown_key" => true]));

var_dump(ini_get("session.cookie_secure"));
var_dump(ini_get("session.cookie_samesite"));
var_dump(session_set_cookie_params(["secure" => true, "samesite" => "please"]));
var_dump(ini_get("session.cookie_secure"));
var_dump(ini_get("session.cookie_samesite"));

var_dump(ini_get("session.cookie_lifetime"));
var_dump(session_set_cookie_params(["lifetime" => 42]));
var_dump(ini_get("session.cookie_lifetime"));

echo "Done";
ob_end_flush();
?>
--EXPECTF--
*** Testing session_set_cookie_params() : array parameter variation ***

Warning: session_set_cookie_params(): No valid keys were found in the options array in %s
bool(false)

Warning: session_set_cookie_params(): Unrecognized key 'unknown_key' found in the options array in %s

Warning: session_set_cookie_params(): No valid keys were found in the options array in %s
bool(false)
string(1) "0"
string(0) ""
bool(true)
string(1) "1"
string(6) "please"
string(1) "0"
bool(true)
string(2) "42"
Done
6 changes: 2 additions & 4 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1431,23 +1431,21 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setcookie, 0, 0, 1)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expires)
ZEND_ARG_INFO(0, expires_or_options)
ZEND_ARG_INFO(0, path)
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)
ZEND_ARG_INFO(0, name)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, expires)
ZEND_ARG_INFO(0, expires_or_options)
ZEND_ARG_INFO(0, path)
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)
Expand Down
Loading

0 comments on commit 2b58ab2

Please sign in to comment.