Skip to content

[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

Closed
wants to merge 2 commits into from
Closed

[RFC] SameSite cookie option #3398

wants to merge 2 commits into from

Conversation

pmmaga
Copy link
Contributor

@pmmaga pmmaga commented Jul 19, 2018

This PR is an implementation of the winning vote in the samesite cookie RFC: https://wiki.php.net/rfc/same-site-cookie

This PR supersedes #2613.

Any feedback on the implementation is welcome.

samesite = Z_STR_P(value);
found++;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should maybe error if key not string?

convert_to_string_ex(lifetime);
found++;
} else if(!strcasecmp("path", ZSTR_VAL(key))) {
path = Z_STR_P(value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're assuming here that the path is a string. Either you need to explicitly check this, or use something like zval_get_string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to use zval_get_string for all of these, however, I'm missing something as it is currently leaking on ext/session/tests/session_set_cookie_params_error.phpt.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, you have to zend_string_release all strings obtained by using zval_get_string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that they are all covered at the moment. The test uses the same variable for all the parameters in sequence. And I think that that's the cause of the leak (as it doesn't leak on any other test where the parameters are constants).

} else if(!strcasecmp("samesite", ZSTR_VAL(key))) {
samesite = Z_STR_P(value);
found++;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should an unknown option generate a warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the warning and the error. Thanks

} ZEND_HASH_FOREACH_END();

/* Array is not empty but no valid keys were found */
if (found == 0 && Z_ARRVAL_P(options)->nNumUsed > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use zend_hash_num_elements() instead of nNumUsed.

ZVAL_DEREF(value);
if(!strcasecmp("lifetime", ZSTR_VAL(key))) {
lifetime = value;
convert_to_string_ex(lifetime);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to modify the array in-place. Either this should have a strict type check, or use zval_get_string (with appropriate refcount management for the other case).

@pmmaga
Copy link
Contributor Author

pmmaga commented Jul 20, 2018

I've fixed the pending issues and implemented the suggestions. Thanks for the feedback

@pmmaga pmmaga changed the title [WIP] [RFC] SameSite cookie option [RFC] SameSite cookie option Jul 20, 2018
php_error_docref(NULL, E_WARNING, "Unrecognized key '%s' found in the options array", ZSTR_VAL(key));
}
} else {
php_error_docref(NULL, E_ERROR, "The options array must only use string keys");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be just E_WARNING. E_ERROR is a fatal error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, downgraded to warning.

RETURN_FALSE;
}
} else {
convert_to_string_ex(lifetime_or_options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convert_to_string_ex call can be dropped if this uses zval_get_string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although unnecessary, I left it behind as the old code was modifying the variable in place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The in-place modification is not externally visible though, so it's safe to drop it.

}
if (samesite) {
ini_name = zend_string_init("session.cookie_samesite", sizeof("session.cookie_samesite") - 1, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Indentation

RETVAL_TRUE;
} else {
RETVAL_FALSE;
}

cleanup:
if (path) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this cause double releases for the non-array codepath?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would indeed, thanks

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)
Copy link
Contributor

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?

Copy link
Member

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.

httponly_null = 0;
found++;
} else if(!strcasecmp("samesite", ZSTR_VAL(key))) {
samesite = zval_get_string(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we want to check that value is either Strict or Lax?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

if (found == 0 && zend_hash_num_elements(Z_ARRVAL_P(options)) > 0) {
php_error_docref(NULL, E_WARNING, "No valid options were found in the given array");
return 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning seems a bit redundant. Isn't it only thrown if one of the above warnings already triggered?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it's true that it will only trigger when one of the others also triggered, I think it has some usefulness to distinguish between some keys being invalid and all keys being invalid. OTOH, it does feel a bit redundant given that it will only show paired with one of the above. If we remove the warning, should we also remove the return 0;? As it stands, the cookie won't be set when this warning is shown.

Copy link
Member

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the internals discussion, I believe we have enough consensus to land this with the implementation proposed here.

Allows using an alternative array argument with
support for the samesite option on the following
functions:
setcookie
setrawcookie
session_set_cookie_params
@cmb69
Copy link
Member

cmb69 commented Jul 31, 2018

Okay, I'm going to merge this PR. However, we'll likely have to work on this as soon as possible, since currently it seems to be possible to pass additional parameters after the options array, e.g.:

<?php
session_set_cookie_params(array('path'=>'/foo/'), 'bar', 'www.example.com');
var_dump(session_get_cookie_params());

outputs:

array(6) {
  ["lifetime"]=>
  int(0)
  ["path"]=>
  string(5) "/foo/"
  ["domain"]=>
  string(15) "www.example.com"
  ["secure"]=>
  bool(false)
  ["httponly"]=>
  bool(false)
  ["samesite"]=>
  string(0) ""
}

@php-pulls
Copy link

Comment on behalf of cmb at php.net:

Thanks! Applied via 08b9310 and 2b58ab2.

@frederikbosch
Copy link
Contributor

@pmmaga @cmb69 @nikic @jvoisin Thank you all for taking over the implementation of the RFC. I am happy that it landed after all in PHP 7.3. Great job!

@jvoisin
Copy link
Contributor

jvoisin commented Oct 30, 2018

For the record, the Snuffleupagus module provides support for the SameSite attribute for PHP7.0, 7.1 and 7.2 :)

Thank you for pushing this feature upstream ♥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants