Link to home
Start Free TrialLog in
Avatar of dsurrett2
dsurrett2Flag for United States of America

asked on

Cookies sent/received between jQuery Ajax and PHP

I am working on an Ajax application that uses jQuery to send requests to PHP scripts. After typing his credentials in, the user hits the "Sign In" button, which sends his credentials to a PHP script that either starts his session and puts the key into a cookie and prints out JSON-encoded "true", or prints out JSON-encoded "false." The cookie is successfully loaded into the browser, and appears correct when viewed in Firecookie.

No problem so far. However, when I attempt to then make another AJAX request and continue the session, it does not appear that the cookie (which is set to last for half an hour currently, but its lifetime will be refreshed as long as the user continues working) is being sent properly by jQuery Ajax. Opening up my request in the Firebug console, the cookie sent has the same value, but is lacking its "domain", "path", and "expires" attributes.

This seems to result in my PHP not behaving correctly, as it is not pulling previous session information out of the database because it thinks it doesn't have the right cookie (because it is missing essential data). However, a new cookie is not created by "session_start."

Any thoughts?
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

the cookie sent has the same value, but is lacking its "domain", "path", and "expires" attributes. -- This sounds like normal cookie behavior.  The browser keeps track of this information, and uses it to decide what cookies to send, but does not send it back to the server.  The server gets an associative array with a cookie name and a cookie value.  Run this and you can see what is there.
<?php // RAY_temp_dsurrett2.php
echo "<pre>";
var_dump($_COOKIE);

Open in new window

Avatar of Dave Baldwin
Maybe you have something out of sequence.  Every single PHP page that is part of the session must have "session_start" at the top of the page.  Any page that does not have that will not be able to access $_SESSION variables for that session.  You can still use $_SESSION variables but they will not be the ones for the session started and maintained by "session_start".
Avatar of dsurrett2

ASKER

@Ray, then how does PHP know it's the right cookie? I assumed it checked those values to ensure that it was receiving the right cookie for its session id value, and that's why it was having issues. Perhaps I had the browser and PHP side mixed up, but then why would PHP not be recognizing the cookie? The cookie exists in the PHP script, as I'm running code conditionally only if it is set. For some reason though, PHP doesn't seem to be treating it as the session id even though I have it set that way in php.ini.

@Dave, yes, I am using session_start() first on each php script. On the first one, I then place two values into the session, and on the second, I place another value into it and try a var_dump(). Only the third value placed in is shown though.

Just thought of something. Do I have to call session_id() with my custom session id every time before I do session_start() if I'm using a custom session id? I thought that since the cookie was already created, and PHP was told in the ini that the cookie is where the session id is, I wouldn't need to use that after the initial time. Perhaps I'm wrong.
how does PHP know it's the right cookie? - great question.  The answer is very simple: PHP does not know.  It's not part of the HTTP architecture for the server to distrust the client.  Obviously this creates some security implications!

I didn't know you were using a custom session id.  Why are you using one?
See here: http://us.php.net/manual/en/function.session-id.php

"If id is specified, it will replace the current session id. session_id() needs to be called before session_start() for that purpose. "

Since the "session_id" is the value that is set in the session cookie, it is the value that session_start() is looking for to identify the current session.  Which means that "session_id" must be unique for every new session...
Just reading the user-contributed notes here tells me that there is a lot of confusion about how sessions and cookies work.
http://www.php.net/manual/en/function.session-id.php

So I scanned my code library for the string session_id and out of thousands of scripts it appeared only four times.  The examples that required session_id() were arcane things like setting session cookies that would work across sub-domains, or restricting scripts to run behind HTTPS only.

Maybe you do not need the session_id() function at all?  In practice, all you need may be session_start()  If a session is started automatically and with minimal "extra" code, the session usually works correctly.
I agree with Ray, I would not use session_id() when session_start() does all you need.  To use session_id(), you need a routine that generates unique session_id()'s ahead of the session_start() line.  Of course it need to check the cookies first to see if one is already set.  session_start() already does all of that.
Dave: here is one of the very rare examples that made sense for me to use session_id().  By default the PHP session handler sets the cookie only for the current sub-domain.  So if you want a cross-sub-domain cookie, this is the way to get it.  All the best, ~Ray
<?php // RAY_session_cookie_domain.php
/* *
 * QUESTION: WHEN CLIENTS VISIT MY SITE SOMETIMES THEY USE www.mysite.org
 * BUT SOMETIMES THEY USE mysite.org WITHOUT THE WWW.  HOW CAN I HANDLE
 * THE SESSION ISSUES THAT ARISE FROM THIS?
 *
 * ANSWER: ONE WAY IS TO REWRITE THE URL TO REMOVE THE SUBDOMAIN IF IT
 * IS WWW.  FOR EXAMPLE:
 *
 *     Options +FollowSymlinks
 *     RewriteEngine on
 *     RewriteCond %{http_host} ^www\.example\.org [NC]
 *     RewriteRule ^(.*)$ http://example.org/$1 [R=301,NC]
 *
 * ANOTHER WAY IS TO MODIFY THE SESSION COOKIE SO IT WORKS ACROSS ALL OF
 * YOUR SUBDOMAINS.  YOUR CHOICE WILL LARGELY DEPEND ON THE WAY YOU WANT
 * TO HANDLE OTHER SUBDOMAINS (OTHER THAN WWW).
 */

// DEMONSTRATE HOW TO START SESSIONS THAT WORK IN DIFFERENT SUBDOMAINS PHP 5.2+
error_reporting(E_ALL);


// MAKE THE SESSION COOKIE AVAILABLE TO ALL SUBDOMAINS
// MAKE A DOMAIN NAME THAT OMITS WWW OR OTHER SUBDOMAINS
// BREAK THE HOST NAME APART AT THE DOTS
$x = explode('.', strtolower($_SERVER["HTTP_HOST"]));
$y = count($x);
// POSSIBLY 'localhost'
if ($y == 1)
{
    $host = $x[0];
}
// MAYBE SOMETHING LIKE 'www2.atf70.whitehouse.gov'
else
{
    // USE A DOT PLUS THE LAST TWO POSITIONS TO MAKE THE HOST DOMAIN NAME
    $host = '.' . $x[$y-2] . '.' . $x[$y-1];
}

// START THE SESSION AND SET THE COOKIE FOR ALL SUBDOMAINS
$sess_name = session_name();
if (session_start())
{
    // MAN PAGE http://us.php.net/manual/en/function.setcookie.php
    setcookie($sess_name, session_id(), NULL, '/', $host, FALSE, TRUE);
}


// PROVE THAT THE COOKIE WORKS IN MULTIPLE DOMAINS
// LOAD UP SOME INFORMATION TO SHOW SESSION CONTENTS
$_SESSION["cheese"] = "Cheddar";
if (!isset($_SESSION["count"])) $_SESSION["count"] = 0;
$_SESSION["count"] ++;


// PUT UP TWO LINKS WITH DIFFERENT SUBDOMAINS
// STRIP OFF THE DOT THAT WAS NEEDED FOR SETCOOKIE
$gost = ltrim($host,'.');
$dmn_link = 'http://'    . $gost . '/RAY_dump_session.php'; // var_dump() SCRIPT
$www_link = 'http://www' . $host . '/RAY_dump_session.php';

echo "<br/>Click these links to get a new window and see the _SESSION and _COOKIE arrays" . PHP_EOL;
echo "<br/><a target=\"_blank\" href=\"$www_link\">$www_link</a>" . PHP_EOL;
echo "<br/><a target=\"_blank\" href=\"$dmn_link\">$dmn_link</a>" . PHP_EOL;


// SHOW WHAT IS IN COOKIE AND IN $_SESSION
echo "<pre>";
echo "COOKIE ";
var_dump($_COOKIE);
echo PHP_EOL . PHP_EOL;
echo "SESSION ";
var_dump($_SESSION);
echo "</pre>";


?>
<form method="post">
<input type="submit" value="CLICK ME" />
</form>

Open in new window

I see. We were using "session_id()" to emulate legacy code, since our current "rewrite" is actually more like a "next version." In the old system, we weren't even using the PHP session management; rather, we were manually creating session id's by hashing a random string, and we stored these along with some basic data (user id, last activity time, session start time, session end time, etc) in our database. I have since created custom save handlers, as mentioned before, in order to allow us to use the PHP _SESSION array in our code and have it truly represent the session. However, I had thought it a good idea, for security's sake, to not only hash the string, but to also encrypt it with a random seed, further lowering the likelihood that someone would be able to generate a session id that matches an existing session.

I then manually set this value (changing the name of the cookie PHP uses to store the session id as well) using session_id(). That's the reason for it. For security purposes, however, is it acceptably "safe" to simply use the default php session id generation code? Can this default funcitonality also be overridden, perhaps? Or am I worrying a bit too much?
If you are generating the session_id and the cookie name yourself, you are overriding part of the session control code.  The session cookie name is set in 'php.ini' and I have one server where I have changed that but mostly for my own amusement.  If yo are using session_name() and session_id(), they need to be present on every page before session_start() .  http://us3.php.net/manual/en/function.session-name.php  If you don't do that, the session_start() code will not recognize the correct cookie or session id and will not retrieve the correct $_SESSION variables.

If you use the default session_start() code without using session_name() and session_id(), it will take care of those things 'automatically'.  And that is what most of us do.  The session code in PHP has been around a long time and looked at by a lot of people to try to make sure it works right.  http://us3.php.net/manual/en/book.session.php
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yes, that helps a lot. As a side note, I am not generating the cookie name on the fly; I had simply changed the cookie name in php.ini. Must I, were I to keep what is there, still call "session_name()" with the new cookie name every time? It doesn't retrieve the session name from the cookie name in php.ini?

Thank you for the helpful information and tips. I probably am worrying a bit over-much. The context, however, is a logistics and freight auditing application, to put it in perspective for you, so much of the data is relatively sensitive.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ah. Alright, thank you very much for your help. While working through my code this morning, having switched to using the default session id generation in session_start, I was able to figure out what the real root problem was. Turns out, it was something somewhat unrelated. The sqlsrv drivers that must now be used with PHP 5.3 (which we just switched to for this new version; yes, a lot of changes at once, I know) provide a num_rows function, which seems to not be working right. I had it at several key test points in my session handling functions, and my database _write's and _read's were failing as a result.

Ray and Dave, you both helped me work through one issue and rule it out as the problem, and I apologize for not properly ruling this out in the first place and assuming it was something else. Thank you for your patience, help, and the wealth of information that has taught me much more about PHP sessions.
The difficulty in finding a solution was my fault, and I discovered my error through the help of Ray and Dave.
Thanks and good luck with the sqlsvr driver.  It isn't exactly the same as the mssql driver.  make sure you have the most recent version.  I know that they have updated it at least once.