I am building a website that hosts an online game. In this game there are lots of characters and monsters. Each of these has their own base set of numbers to determine the details of each particular character. I have one master character sheet/management pages to change/modify these characters.
Goal:
I need to load character information once, no matter what page the user goes to or if they refresh the page. If the character doesn't change I do not want the server to recalculate everything. If the character changes stats or they change to a new character I need to recalculate the character.
Current Solution:
I pass the character ID, charID as a URL parameter. A function checks if it a new character or the same. It checks to see when the character was last updated. It also checks to see if there is a cookie "_created_date" and what the date is. If it does not see the cookie, or the character has been updated it builds new cookies for the character information. This function dynamically builds 40-60 cookies with all kinds of information.
Problem:
When the function sees it should rebuild the cookies it does so, but it also logs out the user. I think this is because I am not unsetting the cookies properly. I think I am unsetting the login cookies as well.
Questions:
Is using cookies the right approach to managing all of this information on a client by client basis. I think it is. But it is the first time I have ever done anything on this scale.
What is the best way to manage a group of cookies? In a cookie array? Should they all have the same prefix or something?
How can you delete/unset this group of cookies without messing with any other cookies on the website?
I see cookies being built in /tools and in /tools/edit_character. (Function checks state of cookie on about 6 different pages.) Should all these pages be under the same directory? Will deleting/unsetting cookies in a directory also remove those in child directories?
Should I stick with session vars or give them expiration dates out into the future? In the case of them coming back in a couple days.
I have already seen how passing all these cookies to the client has dramatically saved server resources and time which is great. However, logging my users out all of the time is a terrible user experience that I really need to correct.
Website is on apache server, mysql, php, jQuery, js, ajax, wordpress base (the pages that do this are all custom templates).
Thank you for your suggestions and help. If there is any other information I can provide please let me know.
Here is just a piece of the cookie building function.
function fyxt_setActiveCharCookie ($charID, $fyxtAccountID) {
global $wpdb;
setcookie("charID",$charID);
$charLvl = curBaseCharLvl($charID);
setcookie("charLvl", $charLvl); //sets session var
//Begin to build character information /////////////////////////////////////////////////////////////////////
$isNPC = isNPC($charID);
setcookie("isNPC", $isNPC); //sets session var
if ($isNPC == 1) {
$hordeSize = hordeSize($charID);
setcookie("hordeSize", $hordeSize); //sets session var
$charL1Info = getNPCInfo($charID);
foreach ($charL1Info as $n=>$v) {
setcookie($n, $v); //loop to set vars for all char info
}
}
}
Unset Function
function fyxt_unsetActiveCharCookie () {
foreach ($_COOKIES as $c_id => $c_value)
{
if ((strpos($c_id,'wordpress') !== false) || (strpos($c_id,'wp') !== false) || (strpos($c_id,'phpbb3') !== false) || (strpos($c_id,'__ut') !== false) || (strpos($c_id,'PHPSESSID') !== false)) {
//don't delete
} else {
setcookie($c_id, NULL, 1, "/fyxt-rpg-tools/", ".fyxtrpg.com");
}
}
}
It is not recommend to use cookies as storage for any informations like logic, permissions or sensitive informations (like characters). Use sessions or files and only give an unique id the user in a cookie to refer to this file. But make sure the file cannot be hijacked (I'm refering to Session Hijacking).
To save the informations in a file, use a serialized php array and write it with PHP5 functions to a file and read again from it and save it in an array for further use in your script.
http://ch1.php.net/manual/en/function.serialize.php
http://ch1.php.net/manual/en/function.unserialize.php
http://ch1.php.net/manual/en/function.file-get-contents.php
http://ch1.php.net/manual/en/function.file-put-contents.php
After looking at this and trying a few things I have decided to add an intermediary table to the database. In this table I will store the calculated character stats. Then I can retrieve these easily with a single select.
Also this intermediary table will help to tackle an issue I was having with keeping multiple characters "in memory" for more advanced calculations and comparisons. This way all that is stored in a table that can be referenced instead of storing a bunch of variables either on the server or the user’s computer.
This also seems to have a good impact on server load. I retrieve the character info and rewrite this one row in the table after a level up or other character change. If no change, I can just hold the array that is originally retrieved from this intermediary table.
Related
I want to count image views on my website. Until now, I set a cookie that contains the last 20 viewed images like 1254.12.963.4328.32 and so on.
This is to prevent multiple counting if somebody presses F5 / reloads the page. Also I don't want to count crawlers and spiders. (I somewhere read that bots won't set this cookie, but I don't know)
Would I count only real users, when I use the session id and save an array with the viewed image-ids in the session? I use laravel with database session driver. And how much data can be stored per session?
This is a home-made way to do that. Of course, there are some libraries that do a better job than this. However, this is a good start. Specially, if you want to do it yourself.
<?php
if (!$_SESSION['counter'] || !isCrawler()) {
$_SESSION['counter'] = 'whatever-counter';
}
function isCrawler() {
$crawlers = array('googlebot'); //add more crawlers agents
foreach ($crawlers as $crawler) {
if (strstr(strtolower($_SERVER['HTTP_USER_AGENT']), strtolower($crawler))) {
return true;
}
}
return false;
}
Probably you want to determ by browser.
See in to this project which provide browser list and documentation how to catch it.
BrowsCap
Would I count only real users, when I use the session id and save an array with the viewed image-ids in the session?
No. I guess the answer is no to question if you would count real users because you would have to require some action to verify that it is a real user. That's why - for example captchas are used when submtting forms.
However, you could narrow it down though so you could check "who's" browsing your page. You've got a great answer from Lea Tano which I beiieve would give you a hint where to start.
And how much data can be stored per session?
Sessions are (by default) stored to the server, so the actual limits is determined by PHP's memory_limit() and diskspace (on the server).
I know there are hundreds of these questions but what I am asking however is slightly different.
When the user logs in I would like to get all their data from each table in a database and store it in a session variable (obviously not sensative data such as encrypted password/salts etc basically data that would be useless or have no value to a hacker!!), and whilst the user uses the website the relevant data stored in the session will be used as opposed to accessing the database everytime. Moreover when the data is changed or added this will be written or added to the session file, and upon a major action such as "saving" or "loggin out" the new/changed data will be written to the database.
The reason I wish to do this is simply for efficieny, I want my application to not only be fast but less resource consuming. I am no expert on either which may explain why my idea makes no differnece or is more resource intensive.
If there is an alternative to my solution please let me know or if there is something to improve on my solution I will be glad to hear it.
Thank you.
My application is using PHP and mysql.
If any of these don't apply to your app, then please ignore. In general, I'm against using sessions as caches (especially if anything in the session is going to be written back to the DB). Here's why.
Editing the session requires a request from the user. Editing a php session outside of the request-response cycle is very difficult. So if a user Alice makes a change which affects Bob, you have no way to dirty Bob's cache
You can't assume users will log out. They may just leave so you have to deal with saving info if the session times out. Again, this is difficult outside of the request-response cycle and you can't exactly leave session files lying around forever until the user comes back (php will gc them by default)
If the user requires authentication, you're storing private information in the session. Some users may not be happy about that. More importantly, a hacker could imploy that private information to conduct a social engineering attack against the end-user.
Mallory (a hacker) might not be able to use the information you put in the session, but she can poison it (ie. cache poisoning), thereby causing all sorts of problems when you write your cache to your permanent storage. Sessions are easier to poison then something like redis or memcache.
TL;DR Lots of considerations when using a session cache. My recommendation is redis/memcache.
You can also go for local-storage in HTML5, check The Guide and THE PAST, PRESENT & FUTURE OF LOCAL STORAGE FOR WEB APPLICATIONS
Local Storage in HTML5 actually uses your browsers sqlite database that works as cookies but it stores data permanently to your browser
unless someone by force remove the data from the browser finding the data files
Or if someone remove/uninstall browser completely,
or if someone uses the application in private/incognito mode of the browser,
What you need to do
Copy the schema for required tables and for required columns and update data at a regular interval
you dont have to worry about user's state, you only have to update the complete data from the localStorage to mysql Server (and from the mysql server to localStorage if required) every time user backs to your application and keep updating the data at regular interval
Now this is turning out to be more of localStorage but I think this is one of the best solution available for me.
redis is a good solution if it is available for you (sometimes developers can't install external modules for some reason) what I would do is either go with your Session approach but with encoded/encrypted and serialized data. Or, which I really prefer is to use HTML5 data properties such as:
<someElement id="someId" data-x="HiX" data-y="Hi-Y" />
which BTW works fine with all browsers even with IE6 but with some tweaks, specially if your application uses jquery and ajax. this would really be handful.
You need to use Memcache for this kind of work. To solve the problem of keeping the updated data everywhere you can create functions for fetching the data, for example when the user logs in you, authenticate the user and after that insert all the user data into the memcache with unique keys like :-
USER_ID_USERNAME for user's username
USER_ID_NAME for user's name
etc...
Now create some more functions to fetch all this data whenever you need it. For ex
function getName($user_id){
if(Memcache::get($user_id."_name"){
return Memcache::get($user_id."_name");
} else {
//Call another function which will fetch the data from the DB and store it in the cache
}
}
You will need to create functions to fetch every kind of data related to the user. And as you said you want to update this data on some major event. You can try updating the data using CRON or something like that, because as tazer84 mentioned users may never log out.
I also use what the OP described to avoid calls to db. For example, when a user logs-in, i have a "welcome-tip" on their control panel like
Welcome, <USERS NAME HERE>
If i stored only his user_id on $_SESSION then in every pageview i would have to retrieve his information from the database just to have his name available, like SELECT user_name FROM users WHERE user_id = $_SESSION['user']['user_id'] So to avoid this, i store some of his information in $_SESSION.
Be careful! When there is a change on data, you must modify the data in db and if successfull also modify the $_SESSION.
In my example, when a user edits his name (which i also store in $_SESSION so i can use it to welcome-tip), i do something like:
If (UpdateCurrentUserData($new_data)) // this is the function that modifies the db
{
$_SESSION['user']['user_name']=$new_data['user_name']; // update session also!
}
Attention to:
session.gc_maxlifetime in your php.ini
This value says how much time the $_SESSION is protected from being erased by the garbage collector (the file that exists on your disk in which the $_SESSION data are stored)
If you set this very low, users may start getting logged-out unexpectedly if they are idle more than this amount of time because garbage collector will delete their session file too quickly
if you set this very high, you may end up with lots of unused $_SESSION files of users that have left your website a long time ago.
also i must add that gc_maxlifetime works together with session.gc_probability where in general you need lower probability for high-traffic websites and bigger probability for lower traffic since for each pageview there is a session.gc_probability that garbage collector will be activated.
A nice more detailed explanation here http://www.appnovation.com/blog/session-garbage-collection-php
I know this sounds stupid but ....
If ur data is not sensitive the best way to make it accessible faster is to store it in hidden variables inside the forms itself. You can save comma separated or values in an array.
what I am trying to find out is, if I can share a Session variable for multiple clients.
Like they can use the exactly same Object. The below example will illustrate what I would like to do.
client1:
start_session();
include('somelcass.php');
//some code...
$someobj = new someclass();
$_SESSION['myobject'] = serialize($someobj);
$id = sha1("somephrase");
set_session_var_for_other_users_by_id('myobject', $id);
client2:
start_session();
include('somelcass.php');
$id = sha1("somephrase");
get_sessionvars_from_other_users($id);
$someobj = unserialize($_SESSION['myobject']);
//now use someobj from class someclass
And my additional question is:
Do you recommand using some session extention like: sessionPsql
Answering your last question first:
The Session PgSQLDocs you linked is the PostgreSQL Session Save Handler. It is a Session Save Handler you can configure to use instead of the default session save handler. The default session save handler in PHP is storing sessions to disk (files). If you use the save handler for PostgreSQL sessions are saved into a PostgreSQL database instead (pgsql).
Saving sessions inside a database can make sense if you want to allow access to the session store from multiple webservers (scaling an application) or in your case (probably) to access all sessions with SQL queries albeit normally a tailored session save handler is defined for that (which could be based on the PgSQL session save handler functions).
To answer your first question then:
Yes you can do so as long as you've got a reference to the object you relate to and you know how to access it. This can be either done by manually accessing the session storage or by sharing a session on it's own and switching sessions to access other session data. It depends on your needs, in your case it's probably more easy to just access serialized data that is stored by the ID in some extra table that has nothing to do with sessions. You should think about how to take care of the data if you don't need it any longer, e.g. remove it after some time of inactivity. In the end you're writing your own session implementation that way which is do-able. PHP before version 4 had no session support out of the box and the session support it has nowadays is very lightweight so if you need to do more specific stuff like you need to do, you normally write your own.
So multiple clients can use the same session (share a session) which is actually as well a way to attack webapps (session hijackingAttack) but as long as the "hijack" is intended inside your application data-flow, I do not see anything technically wrong with it. In PHP that means you need to close the current session, open the other one (sessions are identified by their name and ID), read the value, close the other session and re-open the current one. It technically works in PHP however write solid code when you do this because session problems are quite hard to debug.
This is also often a good reason to write your own object-sharing mechanism between multiple clients instead of re-using PHP's session featureDocs for that.
Multiple clients can't share data in the session object. If you want to share data between clients, you would normally use some other means of server side storage e.g. A database.
I have written a solution for PHP applications to resolve mainly 2 types of problem:
How to share data/variables between PHP Process, hosted on same/differents servers
How to synchronize read/write operations in data/variables
My Project is hosted in GitHub ANYEM Project
First : Start the ANYEM_SERVER using command line
php ANYEM/ANYEM_SERVER/anyem.app.server.impl/ServerImpl.php
Now, in your PHP Application you can do as follow:
<?php
// load server's connection configuration (ANYEM_SERVER IP and Port Number ...)
$clientConnection = ClientConnectionImpl::newClient();
// build a key for your variable that will be stored in server
// the key is composed on 3 Parts : [1] => URL, [2] => Variable Namespace, [3] => Variable Name
$identifier = new ResourceIdentifierImpl("anyem.com", "anyemNameSpace", "a");
$a = 5;
$anyemClient = new AnyemClientImpl($clientConnection, $identifier);
try {
// if $a is reserved by another PHP Process, so this process
// will sleep (1/10) seconds and retry the reservation of the resource
// here, the max number of reservation attempts is 5, if reservation
// fails, an Exception will be thrown
$responseWrapper = $anyemClient->get($a, 5, 100000);
// here we have reserved the variable $a, and we have the unserialized contents
$a = $responseWrapper->getResource()->getData();
// here, we update the contents, send it to ANYEM_SERVER and releasing the lock (we unreserve it)
$anyemClient->put(++$a);
}
catch (Exception $e) {
print $e->getMessage() . "\n";
continue;
}
Hope that can helps someone :)
I think best solution for this problem is using database. Create a table and store in it. When you need just read data from table. it is fast and easy solution to share data between sessions.
I'm designing a web based php reporting system. It involves multiple pages that insert and update to a mysql server. Basically, I want someone to be able to log on, start a report, then go through a fairly long reporting process step by step. Before they were simply filling out excel spreadsheets. I've basically set up a $_SESSION[$var] = (the auto increment ID) of the score table.
$returnQuery = "Select AssessmentID FROM opsassessment.assessmentscores WHERE Date = '$Date' AND InspectorID = '$inspectorResult2[0]'
AND PlantAssistID = '$assistResult2[0]' AND Plant = '$plantResult2[0]'";
$return = mysql_query ($returnQuery);
$return2 = mysql_fetch_row($return);
$_SESSION["return2"] = $return2[0];
echo "The ID for this session is: " . $_SESSION["return2"];
I then assign the session variable to a variable within each page. Then use that variable to update the assessmentscores table with data from several checkboxes. I have two questions about this:
Is there a "better" way of doing this? vague I know. While the system does work I have a suspiscion that there is an easier or more traditional way of doing it.
How much of a security risk am I running my using session?
Note: this is a closed off network so no one outside the company should be able to acccess the webpages unless the network is already hacked. Also, I've implemented SQL injection prevention such as stripping HTML and special characters.
Any comments and/or feedback would be appreciated.
The session data is as secure as your server is. None of the data stored IN the session is every physically transmitted to the user, unless you chose to do so.
The only session-related data that every is (or SHOULD be) transmitted to the user is the ID of the session.
However, storing the ID number in the session can be problematic. Consider the case where a user starts two reports at roughly the same time. Whichever report is started last will overwrite the ID number of the first report, and now all operations in both windows affect report #2.
Sessions were meant to do EXACTLY what you are doing.
And they are safer then the other options, because their storage is server-side (opposed to cookie storage), so the only way someone can access the sessions is if they have access to the webserver's session directory, and if that happens you have bigger problems to worry about.
It is possible to steal a session, but the attacker would need the user's session cookie.
But keep doing like you are doing, store the row's primary ID to prevent extra queries, it is the way it should be done (I would use the $_SESSION variable directly, instead of copying it to another var)
More on session security on SO:
PHP Session Security
Security of $_SESSION array
I have two pages and I want to pass data to each other.
How can I do this without accessing a DB?
Sessions? Cookies? someother magical way?
If you know how, can you please post sample code?
Thanks
Session variables is one way:
$_SESSION["variable"] = "value";
This variable can then be read/modified by another page.
Also note, that you need to start the session by calling start_session(); at the beginning of your script.
And Cookies are another way... You can also try writing in and out of a file instead of a DB
How does a user get between these two pages? I assume a Form based solution is out of the question...
Amongst the possibilities, here are some that I think about :
You could $_SESSION (see Session Handling) -- if both pages are accessed by the same user, without too much time between the two accesses, so the session doesn't expire.
You could store your data to a file ; that'll work fine if :
The amount of data is big
You want it to persist for a long time
But you'll have to do some cleaning-up by yourself
Another idea would be some external daemon, like memcached
But, as it's a caching engine, it's not necessarily good for storing data : the data that is cache can be removed from the cache even if it has not expired yet (i.e. if there is no place left in cache, memcached will remove some least used data)
Of course, if the data is small and you don't mind it going back and forth through the network, and both pages are accessed by the same user using the same browser, you could use cookies
Only a couple of possibilities, though ; my preferences would probably be :
$_SESSION
or files
Depending on your situation.