I store a basket cookie Id which is really uniq, a long random 20 characters string like that: "7irMz2cvpnXpSCkNkBAT"
It's stored on browser not directly in a single Cookie but in a Json global cookie containing multiple values.
I can see Baskets from an admin part and I have sometimes a Basket from let's say a client from New-Zealand falling into the Basket of a someone in India who created an account!
It's all the time the same user stealing the basket Cookie from someone else and putting in it 20 articles which had not been put originally by the client.
I don't know if it's a bot from India or a real person doing that!
How to prevent cookie theft?
I cannot associate a cookie to an IP, people with smartphones for example change sometimes of IP address.
To recover / create / update the cookie I do like that:
// Recovering the Cookies from the Datas Json Object
if(isset($_COOKIE['Datas'])) {
$cookie = json_decode($_COOKIE['Datas']);
if(is_object($cookie)) {
$expiry = $cookie->expiry;
$diff=$expiry-time();
$cookieDatasExpiresDays = floor($diff/(60*60*24));
foreach($cookie->Data as $key => $value) {
$_COOKIE[$key] = $value;
}
} else {
setcookie('Datas', '', -1, '/');
}
}
/**
* Cookie for basket used for non connected users
*/
if((!isset($_COOKIE['Basket']) || $_COOKIE['Basket'] == NULL) &&
!check_bot()) { //We check also if Cookies are enabled
$updateCookie = 'Basket';
$_COOKIE['Basket'] = get_stringSuffle(20, TRUE);
}
if(isset($_COOKIE['Basket'])) {
$cookiesInfos['Basket'] = $_COOKIE['Basket'];
}
...
if($updateCookie != '' && !check_bot()) { //We check also if Cookies are enabled
// Updating Cookie if needed
$cookiesArray = [];
$cookiesArray['Data'] = [];
foreach($cookiesInfos as $key => $val) {
$cookiesArray['Data'][$key] = $val;
}
setcookie('Datas', '', -1, '/');
setcookie('Datas', json_encode($cookiesArray), $expiry, '/');
}
EDIT
As advised by #nice_dev I added the secure options to my call on setcookie:
setcookie('Datas', json_encode($cookiesArray), $expiry, '/', $_SERVER['HTTP_HOST'], TRUE, TRUE);
Note: That's perfect, this modification on the server didn't made me loose any of my Cookie.
Related
I am maintaining the code for an eCommerce website, they use a highly modified version of osCommerce v2.2 RC2. Was noticing an issue where the session isn't started for a new user until they visit the 2nd page of the site.
Looking at the code, before starting the session, it tries to set a cookie. If it detects the cookie it starts the session. Something along this line:
setcookie('cookie_test', 'please_accept_for_session', time()+60*60*24*30, $cookie_path, $cookie_domain);
if (isset($_COOKIE['cookie_test'])) {
session_start();
...
I found an article here that talks about a situation like this, it states:
The first time you only tell the browser to set the cookie, at the time, there is no cookie data in the request header (which could get from $_COOKIE).
Which explains why it takes two page loads for the session to be started. One to set the cookie and one to get notification from the browser that the cookie is set.
My question is, is there anyway around having to go through two page loads to detect the cookie was successfully set on the users browser?
I found this question that didn't really answer my question completely. The highest voted solution was:
setcookie('uname', $uname, time()+60*30);
$_COOKIE['uname'] = $uname;
Which may make it "work" but it doesn't truely tell me that the script was able to set a cookie successfully.
I also found this question, that suggested accessing the headers_list to find the cookie information like so:
function getcookie($name) {
$cookies = [];
$headers = headers_list();
// see http://tools.ietf.org/html/rfc6265#section-4.1.1
foreach($headers as $header) {
if (strpos($header, 'Set-Cookie: ') === 0) {
$value = str_replace('&', urlencode('&'), substr($header, 12));
parse_str(current(explode(';', $value, 1)), $pair);
$cookies = array_merge_recursive($cookies, $pair);
}
}
return $cookies[$name];
}
// [...]
setcookie('uname', $uname, time() + 60 * 30);
echo "Cookie value: " . getcookie('uname');
Which, again, doesn't seem to be verifying that the cookie was set successfully. All this appears to do is search the headers being sent to the browser for the cookie value.
The only solution I can think of is to redirect on the first visit after setting the cookie. Is there any other way?
Here is the answer:
<?php
function set_cookie($name, $value) {
if (!isset($_COOKIE[$name]) || ($_COOKIE[$name] != $value)) {
$_COOKIE[$name] = $value;
}
setcookie($name, $value, strtotime('+1 week'), '/');
}
// Usage:
set_cookie('username', 'ABC'); //Modify the value to see the change
echo $_COOKIE['username'];
I never worked with cookies before so I'm probably messing up really hard.
Thing is. I set a cookie and then try to echo it for testing. If I inspect my browser, it shows me the cookie is there, but the cookie being fetched is always one cookie previous to the one last created. Here is what I'm doing:
function set_cookie_test($user_id)
{
$this->load->helper('cookie');
$this->load->module('site_security');
$this->load->module('site_settings');
$now_time = time();
$one_day = 86400;
$one_week = $one_day * 7;
$one_week_ahead = $now_time + $one_week;
$data['cookie_code'] = $this->site_security->generate_random_string(128); // the cookie_code on the table is 128 chars long
$data['user_id'] = $user_id;
$data['expiry_date'] = $one_week_ahead;
$this->_insert($data);
$cookie_name = $this->site_settings->_get_cookie_name();
set_cookie($cookie_name, $value = $data['cookie_code'], $expire = $data['expiry_date'], $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE);
$cookieVal = get_cookie($cookie_name);
echo $cookieVal; die();
}
This is what happens after I log in (when I call this function)
As you can see, the cookie in the browser has a value of 8XvaQZjWX7... This is the current cookie, it matches my DB.
The previous cookie value in my db is the one being echoed out. I need to figure this out for the following reason:
Whenever I use sessions for the login, I set userdata so I can retrieve the user's info to display his name and avatar on the screen, for example.
I need to be able to do the same when I use cookies...
Any help is very much appreciated.
Is it possible to remove a cookie and set one with the same name but different value in one request? Here is the code I have so far, which works locally for me but not on the live server. The cookie is set from a query string and sometimes the cookie can be set externally (not HttpOnly), but on the live server, I will get a redirect loop and both cookies will be set.
if ($request->query->has('cookie')) {
$setCookie = $request->query->get('cookie');
$route = $request->getRequestUri();
$host = $request->getHost();
if (!$request->cookies->has('reference') || $request->cookies->get('reference') !== $setCookie){
$exp = new \DateTime();
$exp->modify('+3 months'); // cookie has three months to live
$r = new RedirectResponse($route);
$r->headers->clearCookie('reference');
$r->headers->setCookie(new Cookie('reference', $setCookie, $exp, '/', $host));
return $r;
}
}
So if the user goes to www.*******.com/?cookie=newvalue, I need any current cookie with the name 'reference' and a new cookie set with the value, 'newvalue'.
I am getting unknown issue that is i am not able to unset my session with the browser.
Here is my code that checks on every request:
if (isset($_SESSION["logid"])) {
$log = TRUE;
$logid = $_SESSION["logid"];
$session_id = $_SESSION["sid"];
} else {
$log = FALSE;
}
if (!isset($_COOKIE["cook"])) {
if ($log) {
unset($_SESSION["logid"]);
unset($_SESSION["sid"]);
$log = false;
}
$expire = time() + 60 * 60 * 24 * 30 * 2;
$data = array(
"ip" => $_SERVER['REMOTE_ADDR'],
"browser" => $_SERVER['HTTP_USER_AGENT'],
"create_time" => $now
);
$result = $db->insert("cookies", $data);
$cookie_id = $db->lastid;
$cookie_id = my_encrypt($cookie_id);
setcookie("cook", $cookie_id, $expire, "/");
} else {
$cookie_id = $_COOKIE["cook"];
}
and code for logging out is here
unset($_SESSION["logid"]);
unset($_SESSION["sid"]);
I don't want to unset/expire cookie named cook but i want to create it if cookie deleted manually or other reason so the code in first block does that.
Not quite sure I understand the issue, but maybe this will help. All browser instances (windows or tabs) share the same cookie jar. While this is rarely a problem in "real life" it plays havoc with developers because you, as a developer, may have many tabs open. If you want to test an application that changes cookies (such as a login or logout) you should close all instances of your browser and open only one instance, then test.
I am writing a custom session handler and for the life of me I cannot get a cookie to set in it. I'm not outputting anything to the browser before I set the cookie but it still doesn't work. Its killing me.
The cookie will set if I set it in the script I define and call on the session handler with. If necessary I will post code. Any ideas people?
<?php
/* require the needed classes comment out what is not needed */
require_once("classes/sessionmanager.php");
require_once("classes/template.php");
require_once("classes/database.php");
$title=" "; //titlebar of the web browser
$description=" ";
$keywords=" "; //meta keywords
$menutype="default"; //default or customer, customer is elevated
$pagetitle="dflsfsf "; //title of the webpage
$pagebody=" "; //body of the webpage
$template=template::def_instance();
$database=database::def_instance();
$session=sessionmanager::def_instance();
$session->sessions();
session_start();
?>
and this is the one that actually sets the cookie for the session
function write($session_id,$session_data)
{
$session_id = mysql_real_escape_string($session_id);
$session_data = mysql_real_escape_string(serialize($session_data));
$expires = time() + 3600;
$user_ip = $_SERVER['REMOTE_ADDR'];
$bol = FALSE;
$time = time();
$newsession = FALSE;
$auth = FALSE;
$query = "SELECT * FROM 'sessions' WHERE 'expires' > '$time'";
$sessions_result = $this->query($query);
$newsession = $this->newsession_check($session_id,$sessions_result);
while($sessions_array = mysql_fetch_array($sessions_result) AND $auth = FALSE)
{
$session_array = $this->strip($session_array);
$auth = $this->auth_check($session_array,$session_id);
}
/* this is an authentic session. build queries and update it */
if($auth == TRUE AND $newsession == FALSE)
{
$session_data = mysql_real_escape_string($session_data);
$update_query1 = "UPDATE 'sessions' SET 'user_ip' = '$user_ip' WHERE 'session_id' = '$session_id'";
$update_query2 = "UPDATE 'sessions' SET 'data' = '$session_data' WHERE 'session_id = '$session_id'";
$update_query3 = "UPDATE 'sessions' SET 'expires' = '$expires' WHERE 'session_id' = '$session_id'";
$this->query($update_query1);
$this->query($update_query2);
$this->query($update_query3);
$bol = TRUE;
}
elseif($newsession == TRUE)
{
/* this is a new session, build and create it */
$random_number = $this->obtain_random();
$cookieval = hash("sha512",$random_number);
setcookie("rndn",$cookieval,$expires,'/');
$query = "INSERT INTO sessions VALUES('$session_id','0','$user_ip','$random_number','$session_data','$expires')";
$this->query($query);
//echo $cookieval."this is the cookie <<";
$bol = TRUE;
}
return $bol;
}
code updated. still no luck
for some reason if any html is echoed after the session manager is started the cookie is called after the html. this doesnt make any sense to me
The problem is likely in your if/else statements. You are using:
if($auth = TRUE AND $newsession = FALSE)
...
elseif($newsession = TRUE)
The use of a single = means that you are assigning values, not comparing them. You need to use == instead of =.
Change to this:
if($auth == TRUE AND $newsession == FALSE)
...
elseif($newsession == TRUE)
With the code that you have right now, the first if block of your code will run every time, so your setcookie() call is never reached.
setcookie() returns false if php can't add the header. So for debugging try something like
setcookie("rndn",$cookieval) or die('setcookie failed');
You can combine that with a test whether setcookie() is called in the first place
$rc = setcookie("rndn",$cookieval);
/* DEBUG-code don't forget to remove me */
error_log(sprintf("%s %s\n", date('Y-m-d H:i:s setcookie():'), $rc?'success':'failed'));
(or even better use a debugger like xdebug and e.g. netbeans as frontend).
Did you check the response headers in your browser? E.g. via the firebug extension. Perhaps the client receives the cookie header but doesn't accept it.
According to tour code, at least you have to set / directory in the cookie parameters.
But anyway, first of all you have to sniff cookies from the HTTP log. You can use Firebug to watch if server does set any cookie and if browser send any back