I am trying to track the actions of all non-logged in users on my site. The aim is to store this activity so that I can add it to their profile when they do create an account.
I am using the Behaviour below to assign new users a cookie and use that cookie as the basis of a "temp user" row in my Users table. This way a user can straight away start interacting with my API.
This seems to work fine. However, I am seeing loads more "temp user" rows being created in my DB than I have visitors to the site - about 2500 compared with around 500 visits yesterday (according to Google Analytics).
Is there anything wrong with the behaviour below, or am I doing something else wrong? Is there a better way?
<?php
class ApplicationBehavior extends CBehavior
{
private $_owner;
public function events()
{
return array(
'onBeginRequest' => 'setCookies'
);
}
public function setCookies()
{
$owner = $this->getOwner();
if ($owner->user->getIsGuest() && !isset(Yii::app()->request->cookies['dc_tempusername'])):
$tempusername = genRandomString(20);
$tempuser = new User();
$tempuser->username = $tempusername;
$tempuser->email = "noemailyet#tempuser.com";
if (isset(Yii::app()->request->cookies['dc_tempusername'])) {
$tempuser->name = Yii::app()->request->cookies['dc_tempusername']->value;
} else {
$tempuser->name = "CookieBasedTempuser";
}
$tempuser->points = 1;
$tempuser->firstip = $_SERVER['REMOTE_ADDR'];
if ($tempuser->validate()) {
Yii::app()->request->cookies['dc_tempusername'] = new CHttpCookie('dc_tempusername', $tempusername);
$cookie = new CHttpCookie('dc_tempusername', $tempusername);
$cookie->expire = time() + 60 * 60 * 24 * 180;
Yii::app()->request->cookies['dc_tempusername'] = $cookie;
$tempuser->save();
} else {
echo CHtml::errorSummary($tempuser);
}
endif;
}
}
?>
Check if cookies are enabled first:
Check if cookies are enabled
If we're correct, every time you see that the user is a guest and does not have a cookie then you're creating a new temp user.
Why not check to see if a cookie is set first, if so then create the temp user?
You would end up needing to set 2 cookies: initial temp cookie to check against, and then your 'dc_tempusername' cookie.
You could even go as far as using Browscap to check against known bots:
https://github.com/browscap/browscap-php
http://browscap.org/
You'll need to be able to define browscap in your php.ini
Related
I want to make a simple website on a local server that would be accessed by only one device at a time. I've found user management scripts but they are more more complex than what I am searching for. I don't need it to be password protected or have different kind of users and/or rights. Juste a page where only one person at a time can connect.
Is there a way to make it in PHP ?
I've first searched for an option in my server (lighttpd) then for some kind of htaccess but I think PHP is the only way to do it right.
Thank you for your consideration.
According to my comment above:
<?php
$minInterval = 5 * 60; // 5 minutes
$access = true;
if (file_exists('visitor')) {
$visitor = unserialize(file_get_contents('visitor'));
if ($visitor['addr'] != $_SERVER['REMOTE_ADDR']) {
if ($visitor['time'] + $minInterval >= time()) {
$access = false;
}
}
}
if (!$access) {
exit('Access denied.');
} else {
// Update last visitor data
file_put_contents('visitor', serialize([
'addr' => $_SERVER['REMOTE_ADDR'],
'time' => time()
]));
}
I read around that there is a way to keep checking every 30sec or so if the user is still active on the website and if not, logout (or do something else)
I have a basic inactivity logout but it only works if the user is on the website, but if the user closes the browser/tab, it won't work
if (isset($_SESSION['time'])) {
$elapsed_time = time() - $_SESSION['time'];
if ($elapsed_time >= 900) {
mysql_query("UPDATE `users` SET `status` = '0' WHERE `user_id` = '$session_user_id'");
session_destroy();
header('Location:index.php');
}
}
$_SESSION['time'] = time();
how can i do it so that the status changes to 0 only after X amount of inactive time (but it doesn't necessarily have to log the user out - just change the status)?
hook the session save handler functions, specifically the garbage collection one.
While the below examples show deleting, you could easily modify it to just set an inactivity flag, which can then be read from your inactivity script.
session_set_save_handler docs
PHP <5.4 From: http://www.sitepoint.com/writing-custom-session-handlers/
session_set_save_handler("open", "close", "read", "write", "destroy", "garbage");
function gc($lifetime) {
$db = new PDO("mysql:host=myhost;dbname=mydb", "myuser", "mypassword");
$sql = "DELETE FROM session WHERE session_lastaccesstime < DATE_SUB(NOW(), INTERVAL " . $lifetime . " SECOND)";
$db->query($sql);
}
//below ones are covered in the article.
function open(){}
function close(){}
function read(){}
function write(){}
function destroy(){}
Php 5.4+ From: http://www.php.net/manual/en/class.sessionhandlerinterface.php#example-4769
Note: this example is file based you would just have to modify it to use database
<?php
class MySessionHandler implements SessionHandlerInterface
{
public function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
//below functions are covered in the manual
public function open($savePath, $sessionName){}
public function close(){}
public function read($id){}
public function write($id, $data){}
public function destroy($id){}
}
$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
Both rely on a good number of users visiting your site. Otherwise if for instance you dont get users for days the like garbage collection function will not be run for days. In cases like that you would have to setup some other system like a cron job to trigger a script every so often.
Delete the files with the following cron:
find .session/ -amin +20 -exec rm {} \;
This will delete session files that have not been accessed for 20 minutes. This will delete all PHP sessions, assuming you are the only person on the server.
More ways to do this are specified in this question: cleanup php session files
I'm writing a simple website which allows a user to login, fill out a form which is submitted to a database and then log out. In order to manage the session, I used the session manager which is described by TreeHouse on the following page: http://blog.teamtreehouse.com/how-to-create-bulletproof-sessions
In order to protect against hijacking, the client's IP address and user agent are stored in the session variable and compared to the server's values for these properties on each page. If they don't match, then it is assumed that the session has been hijacked and it is reset.
The implementation seems to work on my local machine without any issues, but when I uploaded it to the server, each page refresh causes the preventHijacking() function to return false (meaning it believes the session has been hijacked). However, if I echo any text within that function, the problem mysteriously disappears and the whole thing works as I expect it to (except for the bit of echoed text which is now displayed above my form :P).
I haven't a clue why this would be the case and I can't figure out how to fix it. The session manager code is below. At the start of each page, I use this to start the session and then each page simply uses or sets whatever variables it requires. If anyone could suggest why the function always returns false unless it echoes text and perhaps suggest what modification I need to make so that it will behave in the expected manner, I'd really appreciate it.
<?php
class SessionManager {
protected static $timeout = 600; // Time before automatic logout for the session
static function sessionStart($name, $limit=0, $path='/', $domain=null, $secure=null) {
// Set the cookie name before we start
session_name($name.'_Session');
// Set the domain to default to the current domain
$domain = isset($domain)? $domain : $_SERVER['SERVER_NAME'];
// Set the default secure value to whether the site is being accessed with SSL
$https = isset($secure)? $secure : isset($_SERVER['HTTPS']);
// Set the cookie settings and start the session
session_set_cookie_params($limit, $path, $domain, $secure, True);
session_start();
// Make sure the session hasn't expired and destroy it if it has
if(self::validateSession()) {
// Check to see if the session is new or a hijacking attempt
if(!self::preventHijacking()) {
// Reset session data and regenerate ID
$_SESSION=array();
$_SESSION['IPaddress'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
self::regenerateSession();
// Give a 5% chance of the session ID changing on any request
} else if (rand(1, 100) <= 5) {
self::regenerateSession();
}
$_SESSION['LAST_ACTIVITY'] = time();
} else {
$_SESSION = array();
session_destroy();
session_start();
}
}
static function preventHijacking() {
if(!isset($_SESSION['IPaddress']) || !isset($_SESSION['userAgent'])) {
return false;
}
if($_SESSION['IPaddress'] != $_SERVER['REMOTE_ADDR']) {
return false;
}
if($_SESSION['userAgent'] != $_SERVER['HTTP_USER_AGENT']) {
return false;
}
return true;
}
static function regenerateSession() {
// If this session is obsolete, it means that there already is a new id
if(isset($_SESSION['OBSOLETE']) && $_SESSION['OBSOLETE'] === True) {
return;
}
// Set current session to expire in 10 seconds
$_SESSION['OBSOLETE'] = True;
$_SESSION['EXPIRES'] = time() + 10;
// Create new session without destroying the old one
session_regenerate_id(false);
// Grab current session ID and close both sessions to allow other scripts to use them
$newSession = session_id();
session_write_close();
// Set session ID to the new one and start it back up again
session_id($newSession);
session_start();
// Now we unset the obsolete and expiration values for the session we want to keep
unset($_SESSION['OBSOLETE']);
unset($_SESSION['EXPIRES']);
}
static protected function validateSession() {
// Check if something went wrong
if(isset($_SESSION['OBSOLETE']) && !isset($_SESSION['EXPIRES'])) {
return false;
}
// Test if this is an old session which has expired
if(isset($_SESSION['EXPIRES']) && $_SESSION['EXPIRES'] < time()) {
return false;
}
// Check if the user's login has timed out
if(isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY']) > self::$timeout) {
return false;
}
return true;
}
}
?>
I could be way out here (it's been a while) but that sounds like the buffer containing the headers isn't being flushed for some reason. Providing body would force them to be flushed, so maybe not providing the body doesn't flush?
Try putting ob_end_flush(); in there before you return. That may fix it.
I am using sfGuard as the authentication plugin in my project. I want to invoke certain client side & server side scripts on session timeout. What is the best way I can do this.
Please help!
Thanks a lot.
Well I've been reading the sfGuardSecurityUser and it extends the sfBasicSecurityUser class, which handles user authentication, profile, credentials, etc.
So, I found a function in sfBasicSecurityUser that determines whether a users sessions is timed put called isTimedOut, and also setTimedOut.
If you want to do something when user's session times out, at least on server side, you should listen to the event that is throw when this happens. Check this method:
This could be found in the symfony_core_root_dir/lib/user/sfBasicSecurityUser.class.php
public function initialize(sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
{
// initialize parent
parent::initialize($dispatcher, $storage, $options);
if (!array_key_exists('timeout', $this->options))
{
$this->options['timeout'] = 1800;
}
// force the max lifetime for session garbage collector to be greater than timeout
if (ini_get('session.gc_maxlifetime') < $this->options['timeout'])
{
ini_set('session.gc_maxlifetime', $this->options['timeout']);
}
// read data from storage
$this->authenticated = $storage->read(self::AUTH_NAMESPACE);
$this->credentials = $storage->read(self::CREDENTIAL_NAMESPACE);
$this->lastRequest = $storage->read(self::LAST_REQUEST_NAMESPACE);
if (null === $this->authenticated)
{
$this->authenticated = false;
$this->credentials = array();
}
else
{
// Automatic logout logged in user if no request within timeout parameter seconds
$timeout = $this->options['timeout'];
if (false !== $timeout && null !== $this->lastRequest && time() - $this->lastRequest >= $timeout)
{
if ($this->options['logging'])
{
$this->dispatcher->notify(new sfEvent($this, 'application.log', array('Automatic user logout due to timeout')));
}
$this->setTimedOut();
$this->setAuthenticated(false);
}
}
$this->lastRequest = time();
}
For client side, you might start thinking about HTML 5 and Javascript Workers. The idea could be setting a worker when page loads, and telling him count till session_time_out, then redirecting to a login page or something.
I am currently playing around with cookies in my website, the first thing I do is run a check as to whether the user has cookie, if they dont I display a menu wth 3 options if they click it creates a cookie, but if then quit the browser the cookie is destroyed, here is my code,
function createRandomId() {
$chars = "abcdefghijkmnopqrstuvwxyz023456789";
srand((double)microtime() * 1000000);
$i = 0;
$unique = '';
while ($i <= 7) {
$num = rand() % 33;
$tmp = substr($chars, $num, 1);
$unique = $unique.$tmp;
$i++;
}
return md5($unique);
}
function index() {
// $data is the array of data that is passed to views, setup it up
$data = array();
// We need to setup the cookie that will be used site, this will be used to cross reference
// The user with the options they have selected, to do this we first need to load the session model
// Check if the user has a cookie already, if they it means they have been to the site in the last 30 days.
if(!isset($_COOKIE['bangUser'])) {
// Get createRandomId() method and return a unique ID for the user
$unique = '';
// Setting the cookie, name = bangUser, the cookie will expire after 30 days
setcookie("bangUser", $unique, time() + (60*60*24*30));
$data['firstTime'] = TRUE;
} else {
$data['notFirstTime'] = TRUE;
}
// Load the view and send the data from it.
$this->load->view('base/index', $data);
}
function createCookie() {
// Function gets called when the user clicks yes on the firstTime menu.
// The purpose of this function is to create a cookie for the user.
// First we'll give them a unique ID
$unique = $this->createRandomId();
// With the unique ID now available we can set our cookie doing the same function as before
setcookie("bangUser", $unique, time() + (60*60*24*30));
// Now that the cookie is set we can do a 100% check, check that cookie is set and if it is redirect to
// to the homepage
if(isset($_COOKIE['bangUser'])) {
redirect('welcome');
}
}
Basically the index() function does the check and the createCookie creates a new cookie, can any one see any problems?
In your createCookie function, calling setCookie will not immediately add the value to the $_COOKIE superglobal - this array only holds the cookies present when the request was made (but you could store your new cookie value in the array anyway)
Also, if you want a session cookie which is destroyed when the browser quits, specify null for the expiration time. Alternatively, just use PHP's built in sessions.
You need to set the fourth parameter of setcookie ($path) to the absolute path of you're website. For example:
setcookie("bangUser", $unique, time() + (60*60*24*30), "/");