I have a project that works on a local server but not on my production server, due to cookies not being seen by the server. I've made a minimal version of the code that reproduces the issue on that server:
<?php
if(!isset($_COOKIE['foo'])){
setcookie('foo', 'bar', time() + 7*24*60*60, '/');
echo "Cookie was not found, so we just created it.";
} else {
echo "Cookie was found!";
}
?>
No matter how many times I refresh this page, I always get the "not found" message. Whenever I try to log the $_COOKIE variable, I get an empty Array. However:
The cookie is present in the browser, and correctly sent with the request
The cookie is set and read in the same file (it's not an issue with the path)
There is no output before setcookie, and the file is encoded in UTF8 without BOM
I think this is a server configuration issue, since the code works locally, but I have no idea where to look. Has anyone seen this before, do you know what could cause this?
If you need more info, just tell me and I'll add it to my question. Thank you!
If there's a cache server or CDN involved, it may be filtering cookies based on a whitelist. This is to improve caching, since each request with a unique set of cookies would need to be regarded as different from other requests and could not be cached (you may receive a different reply from the server based on your cookies, so the cache server cannot serve you the cached response of a previous client). Since lots of services are setting cookies which may be sent to the server (e.g. analytics packages) but have absolutely no influence on the contents of the response, heeding all cookies by default would often completely subvert caching.
Configure the caching server in charge to specifically pay attention to your cookie and to forward it to the origin server. If you have a lot of different cookies, consider giving them a common prefix and whitelist that (e.g. foo-*).
I just dealt with the same issue, my production server was allowing me to create a browser cookie using setcookie('cookieNameHere', $cookieValueHere, time() + (86400 * 365), "/"), but the $_COOKIE variable was always an empty array.
The issue was that my production server blocked direct access to the$_COOKIE variable contents via PHP for security reasons.
My solution was to access the cookie value via JavaScript using the following function:
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
I continued to create/update the cookie via PHP.
FYI, I was working on a WordPress site hosted on WP-Engine. See this page for an in depth explanation, and for other options in the event you absolutely need to access a cookie value via PHP (ADMIN-AJAX calls, etc).
I've had similar problem, and it turned out to be HAProxy configuration issue. Do you have any load balancer between the server and the user?
I think your problem is the server time. Check your time with time() function and compare with local time. Maybe one of them is wrong.
Related
Basic situation and basic relevant info:
I have a php code that executes before the opening <doctype> tag. The hope was to (if necessary) send a redirect based on user's browser's language preferences before anything else loads.
The script attempts to do two things based on highest supported language preference:
Use php: setcookie() to create a cookie with the two-letter language code.
Example cookie name = value: x_language = es
Use php: header("Location: " . $requestedSite); to redirect to a subdomain,
Example domain: es.domain.com
Example:
if (isset($_COOKIE['x_language'])) {
-Determine correct subdomain based on cookie value-
-If not currently on that subdomain, redirect to it-
} else {
setcookie('x_language','es',time() + 31536000 ,'/','.domain.com' );
header("Location: " . $requestedSite);
}
The problem:
Firefox works perfectly. Chrome (and other browsers) fail to recognize the cookies at all.
I've boiled it down to this:
print_r($_COOKIE) works properly in Firefox, and returns a lovely, populated array.
print_r($_COOKIE) fails in Chrome, and returns an empty array.
This is the core of the problem, my function doesn't recognize the existence of a cookie because Chrome doesn't.
I've made sure every browser accepts cookies.
I've checked dev tools to make sure the cookie is in place on all browsers, (it is).
I realize a cookie's value isn't available until the next page load, but that isn't an issue here. Even after it is set, it won't read.
There is no output above the initial setcookie();
So how do I get Chrome (and other browsers) to recognize its own cookies?! Does anyone know why this would all work flawlessly on Firefox but fail elsewhere?
On a lark I decided to try this. I created a file that only contains:
<?php
print_r($_COOKIE);
?>
Again, I see the cookie array in Firefox. Meanwhile, in Chrome, IE, Opera, Safari, I get an empty array. Could this be a server issue?
OP returns with answer:
Alright, I'm adding this as an 'Answer' in case anyone else comes across this (totally bizarre) behavior and lands here:
It turns out my hosting provider was doing some seriously aggressive caching with my WordPress site that I was unaware of.
At the time I posted my question, I didn't think being on WordPress was relevant, but apparently it was.
Basically it was doing this:
With a clean Cache:
Visitor 1 visits the site.
The php processes and produces output as expected.
Visitor 1 is served php output (based on his browser's parameters and such).
Visitor 2 visits the site. Visitor 2 sees *Visitor 1's version of the site.
The php is processed once and only once per Cache-clear.
This caching behavior meant that accessing cookies through php was simply not going to work right, but accessing them with Javascript WOULD work.
(Important note: It turns out the above-stated caching behavior is disabled for any user viewing the site while logged into wordpress, and this is common behavior for WordPress Cache plugins. That is why I was seeing different behavior in Firefox than I saw in other browsers, because I was actively logged in with Firefox. This could be a helpful piece of information for someone out there.)
My solution:
Use Javascript to run an AJAX query to a .php file which would process the language preferences of the visitor and return the output as a 2-character code, (i.e. 'en' 'es' 'pt' 'de', etc).
Using AJAX to call php allowed me to use php's server-side access to a browser's language preferences while circumventing the super-agro caching of my host.
I hope this helps someone! And thanks to everyone who tried to help me out with this.
I was not having this problem with the code below. I was able to go to example.com and be redirected immediately to en.example.com and see the cookie in $_COOKIES. If I used en.example.com?set=fr I would be redirected to fr.example.com every time I tried example.com. Hopes this is what you were looking for!
<?php
print_r($_COOKIE);
if(isset($_GET['nuke'])) {
setcookie('x_language','',time()-1000,'/','.example.com');
echo 'It has been nuked!';
exit;
} else if(isset($_GET['set'])) {
setcookie('x_language',$_GET['set'],time() + 31536000, '/','.example.com');
$_COOKIE['x_language'] = $_GET['set'];
}
if (isset($_COOKIE['x_language'])) {
$redirect = $_COOKIE['x_language'].'.example.com';
if($_SERVER['HTTP_HOST'] != $redirect)
header('Location: http://'.$redirect);
} else {
setcookie('x_language','en',time() + 31536000,'/','.example.com');
$redirect = 'http://en.example.com';
header('Location: '.$redirect);
}
echo '<br />Cookie: '.$_COOKIE['x_language'].' Domain: '.$_SERVER["HTTP_HOST"];
?>
I've searched everywhere, but was not able to find any info on this.
Here is the situation:
I am using a PHP file as "HeaderName" for my webspace:
.htaccess has the following:
Options +Indexes
HeaderName /.resource/header.php
in header.php I am including (first thing, before echoing any data) a small include that checks for presence of a parameter and sets a cookie:
if (isset($_GET['css_style']) && $_GET['css_style'] != "") {
$css_style = $_GET['css_style'];
if (setcookie('css_style', $css_style, 0, "/", "xxx.xxxx.xxx") !== false) {
echo "<!-- cookie set! ".$css_style." -->";
} else {
echo "<!-- cookie NOT set! ".$css_style." -->";
}
}
Now, I am using this snippet in other PHP pages on the same site, in any directory, the cookie always get set when I expect it to. It is listed in the Browser's cookie list and contains the desired value.
However, when the same script gets executed as part of the automatic indexing in header.php, I get the "cookie set" output, so I know that the correct conditions all apply, but the cookie is not present in the Browser's cookie list. if I call header.php directly, then the cookie gets set and is present on the browser's list.
So my question: Does Apache do something that might prevent a cookie from being dropped? The site is running on Apache in a hosted environment, but I don't have access to the central server config.
I've tried various different ways to set the cookie, with defaults, with an expire of 0 or time() + 3600*24, with and without a path and domain, no luck. I'm stumped. Especially since it works with a regular file placed in the same directory as the index I am looking at.
You want to decide by to Query Params if you want to sent a cookie
The mod_index creates text/* but no headers
You can use mod_headers for sending cookies:
http://httpd.apache.org/docs/current/mod/mod_headers.html
and mod_setenfif for conditional sending:
http://httpd.apache.org/docs/current/mod/mod_setenvif.html
How can I check whether a request being received is sent from the same server??
Say, I've my domain at www.domain.com. Now I've php processing files which will process forms hosted through this domain. This processes will be executed only if the requests are sent from within the domain ie. www.domain.com and any other requests sent from other domains will be discarded.
Basically : you cannot.
With the HTTP protocol, each request is independent from the others.
A first idea would be to check the Referer HTTP header, but note that :
It can be faked (it's sent by the browser)
It is not always present.
So : not a reliable solution.
A possible, and far better than the Referer idea, solution could be to use a nonce :
When displaying the form, put a hidden input field in it, containing a random value
At the same time, store that random value into the session that correspond to the user.
When the form is submitted, check that the hidden field has the same value as the one that's stored in session.
If those two values are not the same, refuse to use the submitted data.
Note : this idea is often used to help fight against CSRF -- and integrated in the "Form" component of some Frameworks (Zend Framework, for instance).
this will check if there is a referer, then it will compare it with current domain, if different then it is from outside referer
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
}
}
I know this is an old thread, but some one else can probably find it relevant.
The answer is: Yes you can. But it depends if your Apache/nginx server is set to populate the $_SERVER variable with the required information. Most the server are, so probably you can use this approach.
What you need to do is to extract the HTTP_REFERER from the $_SERVER variable and compare with your domain.
<?php
function requestedByTheSameDomain() {
$myDomain = $_SERVER['SCRIPT_URI'];
$requestsSource = $_SERVER['HTTP_REFERER'];
return parse_url($myDomain, PHP_URL_HOST) === parse_url($requestsSource, PHP_URL_HOST);
}
Yesterday I searched almost all topics about php sessions, I looked over manual and it still didn't work. I stayed up very late because of that.
Scenario is: I log in using standard html form. My session is populated with some variables and it work fine. Then I use Flash uploadify to upload some photos and I pass with parametrs - PHPSESSID. My php script does not recognize session. It sees it as empty. Then I try to get that session with different client such as Firefox or Opera and it is empty too. Then I try to get that session with different Chrome tab and it works
As you see my only protection is by IP. I don't scan other things so this should work when good PHPSESSID is passed and client IP is matching regardless of client type, version etc.
This is init_session.php, file I include everytime at the begining of other files. Therefore I know that Session ID is beeing passed with Flash. But then session is empty.
Directory is set on the top, and path is direct so there shouldn't be any problems with that. It also doesn't work when save path is default. I also turned of session autostart and session use only cookies. It didn't change a thing except I need to set cookie manually.
Is there something here I can try? I think I ran out of options.
EDIT:
I forgot about most importat thing i think that turning off suhosin.session.cryptua will resolve case but i can't turn it off using ini_set, is there any other way? It seems that this option encrypts session using user-agent field wich would be the case.
ini_set('session.auto_start', '0');
ini_set('session.save_path','/public_html/nowy/tmp');
if (isset($_POST["PHPSESSID"])) {
session_id($_POST["PHPSESSID"]);
} elseif (isset($_COOKIE['PHPSESSID'])) {
session_id($_COOKIE["PHPSESSID"]);
} elseif (isset($_GET["PHPSESSID"])) {
session_id($_GET["PHPSESSID"]);
}
session_start();
setcookie("PHPSESSID", session_id(), time()+3600, "/");
if (!isset($_SESSION['user'])) {
$_SESSION['user'] = 0;
}
if (!isset($_SESSION['initiate'])) {
session_regenerate_id();
$_SESSION['initiate'] = true;
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
}
echo ini_get('session.save_path').'<br />';
echo $_POST["PHPSESSID"];
print_r($_SESSION);
RESOLVED
This problem has been solved. I couldn't set cryptua off because I needed to copy oryginal php5.ini and replace some variables to get it working.
The problem was as expected suhosin.session.cryptua
As Frode suggested in his comment I'm leaving this as an answer.
This can happen when using any flash script, not just uploadify. Any other flash uploader such as SWFUpload won't get the session unless user-agent field is the same.
This happens when your server has suhosin patch installed but it seems that sometimes this problem doesn't occur even when setting suhosin.session.cryptua is enabled as alecgorge suggested. Although I am not convinced about that. Of course you can pass to flash this variable using php scripts to uncover user-agent of user browser and then flash can disguise himself as the same browser but it's not elegant solution and I don't know actionscript so I can't say if flash can actually do it.
Very important:
There are actually two settings that can fix this security patch.
suhosin.session.encrypt
suhosin.session.cryptua
If the first one is disabled then session is not encrypted at all so problem won't occur. It's not recommended to disable this. If we disable only the second one then session will be encrypted but encryption won't relay on user-agent field. This means that any browser or http client can get any session. Therefore it's recommended to put some other security fields. Suhosin can handle also session ip protection so I recommend to enable suhosin.session.cryptraddr. Other settings can be found here:
Suhosin configuration
To resolve this issue I suggest:
Check phpinfo() if suhosin is installed. If not then problem won't occur.
If suhosin is installed and suhosin.session.cryptua and suhosin.session.encrypt are enabled then copy existing and working php.ini. It's on the top of php info page: Loaded Configuration File /public_html/php5.ini
Create your own php.ini and set:
suhosin.session.encrypt = On
suhosin.session.cryptraddr = On
suhosin.session.cryptua = Off
Usually with Uploadify I have to do something like this:
$('#uploadify').uploadify('scriptData':{'session_name':'<?php echo session_id(); ?>'}});
On the client in conjunction with something like this on the server side:
if($_POST['session_name']) {
session_id($_POST['session_name']);
}
session_start();
I set the cookies regularly in a callback page in my Twitter application. Everything works fine.
Now, using jQuery, I submit a form, and the callback function activates a PHP script. That script only needs to set one cookie to the serialized values of $_POST; and the values run fine (both serialized and normal, I echoed them out to debug). The expiration time is set to 1 year ahead. But for some reason, the cookie just won't appear anywhere. Here's the code:
// js/main.js
$('#settings-form').live('submit', function() {
$.post('controllers/settings.php', $(this).serialize(), function(data) { // Everything here works.
if (data == 'OK') // no errors spits out "OK". this works
changeView({'msg': 'Your settings were saved successfully.'}); // This just resets the view and adds a message div at the top. This works
else
changeView({'msg': data}); // This echoes the error if any exists. Doesn't happen, no errors arise
});
return false; // Cancels redirecting after submitting form
});
// controllers/settings.php
setcookie('user_settings', serialize($_POST), strtotime('+1 year'));
I checked all the variables and I even tried setting dummy ones for test (like "boo" instead of serialize($_POST). for some reason that doesn't work.
Any ideas why this is happening? I tried doing a chdir('..'); to make the cookie dir go to the right place, but that doesn't seem to be the problem, checking the cookies inside my browser doesn't seem to work at all, for any path. It just doesn't work at all. I also just tried manually changing the domain and path, but those don't work either.
Firstly, the chdir() thing is a red-herring -- Cookies are domain-specific; the directory path doesn't have any bearing on them.
Cookies can work a bit strangely when you're making ajax type calls, and I think this is what you're seeing -- The server is probably setting the cookie, but the browser may not be setting it in the cookies data it as it's not a page load.
I would suggest you'd be better off using PHP's session handling rather than cookies; it's better for security, less bandwidth (because the whole of the cookie data is transmitted in both directions with every http single request), and more likely to work.
If you really want to use cookies, it may work better if you use Javascript to do it. You can set cookies in your javascript code by accessing document.cookie. (you need to get the syntax right for the cookie string, but JQuery probably has its own functions that makes them easier to work with)