Setting cookie using header("Set-cookie") vs setcookie() function - php

I'm refactoring some code and found something I've never seen. the function is used for user to set cookie when user logs in:
function setUserCookie($name, $value) {
$date = date("D, d M Y H:i:s",strtotime('1 January 2015')) . 'GMT';
header("Set-Cookie: {$name}={$value}; EXPIRES{$date};");
}
now that I've been assigned to refactor code I'm planning to use setcookie function which essentially does same thing according to php.net.
My question is: is there any difference between two and which one should I use?
NOTE: this code was written long time ago so I'm assuming that at that time setcookie didnt exist?

There's no good reason not to use setcookie. The above code doesn't properly encode names and values, so that's at least one major benefit to refactoring.

The difference between the two functions is that header() is the general function for setting HTTP headers while setcookie() is specifically meant to set the Set-Cookie header.
header() therefore takes a string containing the complete header, while setcookie() takes several cookie-specific arguments and then creates the Set-Cookie header from them.

Here's a use case in which you can't use setcookie
you run a website on PHP<7.3
you have to set 'SameSite' cookie attribute
You can achieve that by exploiting a bug in setcookie, but I wouldn't rely on a bug as it gets fixed over time: setcookie('samesite-test', '1', 0, '/; samesite=strict');
Or you can use PHP header function: header("Set-Cookie: samesite-test=1; expires=0; path=/; samesite=Strict");
Note that secure option is required when setting samesite attribute

One big difference is, that setcookie always sets host_only=false and there is nothing you can do about it.
So if you have to set host_only=true for whatever reasons you have to use the header method. As far as I know.

I replicated what I believe to be the exact behavior of setCookie programmatically. Here is my implementation, if it can be useful for anyone else.
function setUserCookie($name, $value, $expires = 0, $path = "", $domain = "", $secure = false, $http_only = false) {
$value = rawurlencode($value);
date_default_timezone_set('UTC');
$date = date("D, d-M-Y H:i:s",$expires) . ' GMT';
$header = "Set-Cookie: {$name}={$value}";
if($expires != 0) {
$header .= "; expires={$date}; Max-Age=".($expires - time());
}
if($path != "") {
$header .= "; path=".$path;
}
if($domain != "") {
$header .= "; domain=".$domain;
}
if($secure) {
$header .= "; secure";
}
if($http_only) {
$header .= "; HttpOnly";
}
header($header, false);
}
The difference with your function are exactly the difference with setCookie (more arguments like custom expires, path, domain, secure and httpOnly). Especially, note the second argument to "header" (false) so that it becomes possible to place multiple cookies with different calls to the function.

Related

How to convince Zend Framework to send duplicate headers?

With Content-Security-Policy headers there is often a need to send more than one such header or to union merge these headers before sending them. This arises from the fact that each module/package of an application may define its own CSP.
Right now ZF3 doesn't seem to have a way to handle such a scenario. If I try to add multple CSP headers, they keep overwriting each other so that only the last added header is sent.
Code to reproduce the issue
$headers = $controller->getResponse()->getHeaders();
$headers->addHeader(new ContentSecurityPolicy($someDirectives));
$headers->addHeader(new ContentSecurityPolicy($someOtherDirectives));
Expected results
The expected result is a response with two CSP headers (OR a union merged CSP).
Actual results
The second addition overwrites the first, the response only contains that one CSP.
Question
How can I make ZF3 send multple headers with the same fieldname?
For more information about this problem, also see my own issue on github https://github.com/zendframework/zend-http/issues/159
You should be able to create a simple workaround using GenericMultipleHeader as a reference (and changing comma delimiter to semicolon):
class MultiContentSecurityPolicy extends ContentSecurityPolicy implements MultipleHeaderInterface {
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
if (strpos($fieldValue, ';')) {
$headers = [];
foreach (explode(';', $fieldValue) as $multiValue) {
$headers[] = new static($fieldName, $multiValue);
}
return $headers;
} else {
$header = new static($fieldName, $fieldValue);
return $header;
}
}
public function toStringMultipleHeaders(array $headers)
{
$name = $this->getFieldName();
$values = [$this->getFieldValue()];
foreach ($headers as $header) {
if (! $header instanceof static) {
throw new Exception\InvalidArgumentException(
'This method toStringMultipleHeaders was expecting an array of headers of the same type'
);
}
$values[] = $header->getFieldValue();
}
return $name . ': ' . implode(';', $values) . "\r\n";
}
}
Then use that class instead of ContentSecurityPolicy:
$headers = $controller->getResponse()->getHeaders();
$headers->addHeader(new MultiContentSecurityPolicy($someDirectives));
$headers->addHeader(new MultiContentSecurityPolicy($someOtherDirectives));
Since Zend checks the interface rather than the class, should work fine.
This is the accepted HTTP standard and the PHP Core upholds this. http://php.net/manual/en/function.header.php
If you set headers in PHP header("TESTHeader: Test1"); header("TESTHeader: Test2") only one will come through and this is correct to specification RFC2616 Section 4.2 Page 31&32
If you wish to send multiple values your header should construct as header("TESTHeader: Test1, Test2");. while it is possible to send multiple same name headers through PHP it is not recommended as browsers & servers receiving 2 sets of the same header should convert them to the above style this could cause problems as you will not know for certain what format they are in. header("TESTHeader: Test1", false); header("TESTHeader: Test2", false). depending on the server or clients adherence to the RFC or HTTP Version.
So this answer is the reason as to why you are not allowed to send the same header multiple times in ZF3, it can't identify when to use the overwrite or not to based on you setting the header. to get around this and use multi-valued headers you can use Jim's answer
make your own multipleheader class, add the function you need (MultipleHeaderInterface) then add your header in a multistering and finally call it in your
$headers = $controller->getResponse()->getHeaders();
(call the new function with the new fromStringHeaders)

PHP delete cookie that starts with wp_postpass_

I need to write some custom code for a Wordpress function, and I need to be able to delete any cookies that start with wp-postpass_. I know this can be done with jQuery but I'm unsure of how to approach it in PHP.
I have tried Googling and searching on here but I haven't been able to find anything that matches what I'm trying to do.
Thank you in advance,
Andy
EDIT:
Sorry, I should have mentioned that Wordpress appends a random string onto the end of wp-postpass_ hence why I need to find any cookies that start with wp-postpass_. Apologies, early morning.
So iterate through all cookies and check if there contain wp_postpass_ and then remove the cookie.
foreach($_COOKIE as $cookieKey => $cookieValue) {
if(strpos($cookieKey,'wp-postpass_') === 0) {
// remove the cookie
setcookie($cookieKey, null, -1);
unset($_COOKIE[$cookieKey]);
}
}
If you have access to the $_COOKIE superglobal just do
$past = time() - 86400;
foreach($_COOKIE as $name => $value) {
if(strpos($name, 'wp-postpass_') === 0) {
setcookie($name, '', $past);
unset($_COOKIE[$name]);
}
}

php cookie not rewrite the new value

Good morning,
I've got such problem. I would like to store in cookies language, which user will choose. The value in local variable is still changed, but value in cookie is always the same. Even if I always delete the cookie and then I create it again the value stored in cookie is wrong and my local is good. Here's my code:
<?php
if (isset($_GET['lng'])) {
$lng = $_GET['lng'];
if (($lng != "en") && ($lng != "de")) {
$lng = "en";
}
} else {
if(!isset($_COOKIE['lang'])) {
$lng = "en";
} else {
$lng = $_COOKIE['lang'];
}
}
if(isset($_COOKIE['lang'])) {
setcookie("lang", $_COOKIE['lang'], time()-10); //here I try to remove cookie and then create another
}
setcookie("lang", $lng, time()+5);
print_r($_COOKIE);
echo $lng;
?>
print_r will allways return me the main language (en), even if in variable $lng there is de. I think, there will be just stupid problem, but I can't fix it. This removing line (which I commented) is there because of problem written on official php site:
Be careful of using the same cookie name in subdirectories. Setting a simple cookie
setcookie("region", $_GET['set_region']);
both in the root / and for instance in this case /admin/ will create 2 cookies with different paths. In reading the cookies back only the first one is read regardless of path.
And I thought I've similar problem. But this didn't fix my problem and even when the cookie after 5 second will expire to cookie is then written again the bad "en" value.
Thank for your answer
Can you try this, it works for me.
I edited the code a bit, sorry for obfuscating it with one line if-statements.
I also set the cookie time to 1 day so it won't disappear when testing the code.
And remember, you have to update the page to read the new cookie, it will be one step behind $lng.
<?php
$allowed = array('en', 'de');
$chosen = $_GET['lng'] ? $_GET['lng'] : ($_COOKIE['lang'] ? $_COOKIE['lang'] : 'en');
$lng = in_array($chosen, $allowed) ? $chosen : 'en';
setcookie("lang", $lng, time()+24*60*60, '/');
var_dump($_COOKIE['lang']);
echo $lng;
?>

PHP: Can't Set Cookie

For some reason, I can't seem to set a cookie in one of my PHP files. All of the code works fine, except it refuses to set the cookie. I've placed different versions of cookie setting with different arguments, but it doesn't seem to make a difference. On top of that, I can set a cookie using that same line of code in a separate PHP file in the same directory. I've tried placing setcookie() at different places and I still get the same result. Am I missing something?
<?php
$table_name="lfgs";
$name=$_POST['name'];
$event="[";
$level=$_POST['level'];
$comments=$_POST['comments'];
$hours=$_POST['hours']*60*60;
$minutes=$_POST['minutes']*60;
$time=$hours+$minutes+time();
setcookie("remember", $name, $time, 'www.domain.com', '/');
if(isset($_POST['event'])){
if (is_array($_POST['event'])) {
foreach($_POST['event'] as $value){
$event = $event . "\"" . $value . "\",";
}
} else {
$value = $_POST['event'];
$event = $event . "\"" . $value . "\"]";
}
} else {
$event = "";
}
if($event[strlen($event)-1] == ',') {
$event = substr_replace($event ,"]",-1);
}
$con=mysqli_connect("domain.com","username","password","database");
$req="INSERT INTO $table_name(name, event, level, comments, time) VALUES ('$name', '$event', '$level', '$comments', '$time')";
mysqli_query($con,$req);
mysqli_close($con);
foreach($_COOKIE as $c) {
echo $c . "<br />";
}
?>
Edit: This is ALL the code for the entire file.
According to the php reference, the correct way to use the setcookie function is
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
Didn't you swaped the $path and $domain argument?
Try
setcookie("remember", $name, $time, '/', 'www.domain.com');
try
setcookie("remember", $name, '/', $time);
I dont't understand what you want to do, but it'll not works the way you do.
Always remember: you work with PHP on the server side.
So to set a cookie an then to test if it worked, you need always tow steps (because you are on the server side):
The first step is to set the cookie.
And then during the next request you can check if ur cookie contains in the global $_COOKIE array. if yes then ok, if not then mybe the client/user donsen't allow to set cookies.
If you need to do it in "one step", you should use JavaScript. Something like that:
On submit the form, set the cookie and then propagate the submit action (send the data to the server). JQuery support a good solution to set and read cookies.
Are you sure the php interpreter doesn't send a char before the setcookie() call for some reason? The function send a HTTP header, so it have to appear before any printing on the page.
From my experience, if you have any session active on the page it will not allow you to create PHP cookies. Start a new blank page and test it that way.
You should be able to set a cookie on the new generic page. Then go back to your other page that has a session started on it. Echo the details of that set cookie in the session page and you will get the stored value, no problem.
You can call cookies but you can't seem to create them in an active session page. At least, I can't with my current system settings/config.

setcookie not working

I know there was a TONS of similar quesitons here, but I've tried already all suggestions posted in other questions, and nothing helped.
This is my function:
function makecookie($s, $d) {
if(empty($_COOKIE[#COOKIE_PATH . "[{$s}]"])) {
setcookie(#COOKIE_PATH . "[{$s}]", $d);
}
return true;
}
At the top of the document I got: ob_start(); and at the end of the document I got ob_end_flush();.
When I try:
echo setcookie(#COOKIE_PATH . "[{$s}]", $d);
exit;
It returns 1 what means 'true'. And cookie has been not set.
So why does it happen?
Once the cookie is set, you can retrieve it on next page load with $_COOKIE["name of cookie"]
#COOKIE_PATH doesn't look like valid/sensible PHP. Are you sure that isn't some sort of typo for (say) $COOKIE_PATH?

Categories