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).
Related
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.
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.
Basically, I am trying to make a split-test web app, and I'm a bit confused on how to do this without any race conditions.
Basically, there are 3 pages:
main_page.php
page1.php
page1_alt.php
So the process is as follows:
user visits main_page.php
main_page.php checks for a cookie
a. if there is no cookie create a cookie,
b. check the page the last visitor was sent to
c. send the current visitor to the other page (if last visitor went to page1.php, send this one to page1_alt.php)
I have the cookie issue sorted out, I just want to know what you think is the best method on how to do step 2b. If i write to the db, it would be impractical. If I refer to a text file, it would produce possible race conditions.
EDIT: If you think there's an easier way than starting from scratch, do give me some suggestions.:)
I'm not sure I completely understand why you want to do this, or why writing to the DB would be impractical. If you want to split them with exact precision, this seems to me to be the best possible solution.
If however you just need to split them approximately without DB-access, you could be creative:
if (date('s') % 2 == 1) {
header("Location: page1.php");
} else {
header("Location: page1_alt.php");
}
Of course there is some chance involved in this, but since seconds are equally distributed, if you have enough users, the two groups should be quite close to equal size.
I know google analytic can do split testing, I'm not sure what your end goal is but with GA It will split the users and test which page get's the user to the goal the most. Sorry if this wasn't what you were looking for.
Consider using sessions instead of cookies. Sessions are more flexible and secure way to track user activities.
You should be using sessions and header("Location:...);. From that you can do something like this
if (!isset($_SESSION["var"])) {
header("Location: main_page.php");
}
Where $_SESSION["cookie"] is whatever you want to set it to be. I guess in your case you could set to what page they were at last. Then you can just check if it exists and/or what it is and use header to send them to the appropriate page.
I am designing a simple drag and drop quiz. We are limiting the number of attempts to get a correct answer to two for each box/answer. However, I'd like to keep the functionality and state of the quiz separate from the display/view.
Currently, as the user attempts to get a correct answer by dropping an answer box on top of a question box, an ajax call is made to a PHP page which returns a 'true' or 'false' value.
We have been evaluating if we want to use Session variables on the PHP page, cookies, or something even more simple to track how many attempts each box has consumed. It would be preferable (for good form's sake) to somehow maintain the state of this data on the server - so the client has no idea what is going on. Session variables seemed to make sense to me - as the user continues to make attempts with different question/answer combos, the server tracks the number of tries and returns (in the ajax response) the result of a user's question (right/wrong, and how many tries that answer has remaining, if any) but I'm wondering if there's a better solution. Any input?
Session seems like a good fit to me. Cookies can be tampered with so I would avoid that if you need the error count to be accurate.
Sessions is probably your best bet. Cookies could be used as well, or if you can guarantee availability of HTML5 localStorage in the browser, you could use that as well.
Unfortunately to keep the functionality separate, there is not. You can use a session variable, or database storage paired with a session id stored in a cookie.
Store current user state into session (temporary storage) and track what he have answered what not etc. at the last step store data into database, or file (permanent storage). Session is individual for every user. Users can't alter your site sessions.
Session usage
Good day,
I would like to know how to protect my website from ajax-spam. I'm looking to limit any ajax action per
users. Let's say 8 ajax-actions per minute.
An example of an action would be: a button to add/remove a blog posts "as my favorites".
Unless I'm wrong, I believe the best way would be using $_SESSION's variable and to avoid someone/a bot to clear
cookies to avoid my protection. I'm allowing ajax-functions only to logged-on users.
Using database would make my protection useless because it's the unwanted database's writes I'm trying to avoid.
I have to mention that I actually use PHP as server-language and jQuery to proceeds my ajax calls.
Thank you
Edit:
The sentense
... to protect my website ...
is confusing but it's not about cross-domain ajax.
Edit 2011-04-20:
I added a bounty of 50 to it.
Since you're only allowing AJAX actions to logged in users, this is really simple to solve.
Create a timestamp field for each account. You can do this in the database, or leverage Memcached, or alternatively use a flat file.
Each time the user makes a request through your AJAX interface, add the current timestamp to your records, and:
Check to make sure the last eight timestamps aren't all before one minute ago.
From there you can add additional magic, like tempbanning accounts that flagrantly violate the speed limit, or comparing the IPs of violators against blacklists of known spammers, et cetera.
Are you talking about specific ajax-spam to your site, or ajax-spam in general?
If the latter, you can use hashes to prevent auto-sending forms, i.e. write your hash() one-way function which takes string and makes sha1-checksum of it.
So that's how you use it:
// the page is a blog post #357
$id = 357;
$type = 'post';
$hash = hash($_SERVER['REMOTE_ADDR'].$type.$id);
Put that hash in hidden field which is not within the comment form or even hidden div, somewhere at the bottom of the page, and name it "control_hash" or something. Attach it's value to the ajax-request on form submit. When the form is received by the script, make a new hash from $_REQUEST data (excluding existing $control_hash) and check if they match.
If the form was submitted by bot, it won't have $control_hash, so it won't pass.
Yes, your idea in principle is good. Some things to consider though:
If you track the limits globally then you may run into the issue of a bot DoSing your server and preventing legitimate users from being able to use your "Favourite" button.
If you track the requests based on their IP then someone could use a bot network (multiple IPs) to get around your blocking. Depending on your site and what your preference is, perhaps limit based on a subnet of the IP.
Install and use Memcache to store and track the requests, particularly if you are going to be tracking based on the IP. This should be faster than using session variables (someone might correct me on this).
If you have access to the source code of the web-site, you can rewrite some of the javascript code that actually performs AJAX-request. I.e. your pages can have a hidden counter field, that is incremented every time a user clicks the button. And also you can have a timefield hidden on the page, in order to rate the frequency of clicks.
The idea is that you don't even have to send anything to the server at all - just check it on the client side inside the script. Of course, that will not help against the bots adressing directly to the server.
It really depends on the result of such a spam. If you just want to avoid writing to your database, all these check could end up taking more ressources than actually writing to the database.
Does the end justify the means?
You also have to judge what's the probability of such a spam. Most bots are not very smart and will miserably fail when there's some logging involved.
Just my 2 cents, the other answers are perfectly valid to avoid spam.
Buy more powerful hosting to be able serve requests, don't limit them.
8 requests per minute it's ridiculous.
Anyway, if requests are 'legal', you should find ways how to serve requests, not how to limit them. And if not 'legal' - then deny them without any 'time' limitations.
You can use a session field with a global variable holding the time of last ajax request. Since you want to allow 8 requests, make it an array of size 8 and check for the time differences. If it increases, (important) it might not always be a bot. give the user a chance with captcha or something similar. (a math problem maybe?)
once the captcha is validated, allow the next few posts etc..
But do make sure that you are checking for that particular session and user.
Kerin's answer is good, I just wanted to emphasize on captcha.
yes you need to use a function in every function views can interact, also, it should be in global library so you can use it anywhere.
if(is_logged_in())
{
// do you code here
}
while is_logged in is defined as follows
function is_logged_in($activated = TRUE)
{
return $this->ci->session->userdata('status') === ($activated ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED);
}
you should set the status session when user login successfully.