How do I protect against ajax-spam in PHP? - php

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.

Related

How to make sure AJAX is called by JavaScript?

I asked a similar question before, and the answer was simply:
if JavaScript can do it, then any client can do it.
But I still want to find out a way do restrict AJAX calls to JavaScript.
The reason is :
I'm building a web application, when a user clicks on an image, tagged like this:
<img src='src.jpg' data-id='42'/>
JavaScript calls a PHP page like this:
$.ajax("action.php?action=click&id=42");
then action.php inserts rows in database.
But I'm afraid that some users can automate entries that "clicks" all the id's and such, by calling necessary url's, since they are visible in the source code.
How can I prevent such a thing, and make sure it works only on click, and not by calling the url from a browser tab?
p.s.
I think a possible solution would be using encryption, like generate a key on user visit, and call the action page with that key, or hash/md5sum/whatever of it. But I think it can be done without transforming it into a security problem. Am I right ? Moreover, I'm not sure this method is a solution, since I don't know anything about this kind of security, or it's implementation.
I'm not sure there is a 100% secure answer. A combination of a server generated token that is inserted into a hidden form element and anti-automation techniques like limiting the number of requests over a certain time period is the best thing I can come up with.
[EDIT]
Actually a good solution would be to use CAPTCHAS
Your question isn't really "How can I tell AJAX from non-AJAX?" It's "How do I stop someone inflating a score by repeated clicks and ballot stuffing?"
In answer to the question you asked, the answer you quoted was essentially right. There is no reliable way to determine whether a request is being made by AJAX, a particular browser, a CURL session or a guy typing raw HTTP commands into a telnet session. We might see a browser or a script or an app, but all PHP sees is:
GET /resource.html HTTP/1.1
host:www.example.com
If there's some convenience reason for wanting to know whether a request was AJAX, some javascript libraries such as jQuery add an additional HTTP header to AJAX requests that you can look for, or you could manually add a header or include a field to your payload such as AJAX=1. Then you can check for those server side and take whatever action you think should be made for an AJAX request.
Of course there's nothing stopping me using CURL to make the same request with the right headers set to make the server think it's an AJAX request. You should therefore only use such tricks where whether or not the request was AJAX is of interest so you can format the response properly (send a HTML page if it's not AJAX, or JSON if it is). The security of your application can't rely on such tricks, and if the design of your application requires the ability to tell AJAX from non-AJAX for security or reliability reasons then you need to rethink the design of your application.
In answer to what you're actually trying to achieve, there are a couple of approaches. None are completely reliable, though. The first approach is to deposit a cookie on the user's machine on first click, and to ignore any subsequent requests from that user agent if the cookie is in any subsequent requests. It's a fairly simple, lightweight approach, but it's also easily defeated by simply deleting the cookie, or refusing to accept it in the first place.
Alternatively, when the user makes the AJAX request, you can record some information about the requesting user agent along with the fact that a click was submitted. You can, for example store a hash (stored with something stronger than MD5!) of the client's IP and user agent string, along with a timestamp for the click. If you see a lot of the same hash, along with closely grouped timestamps, then there's possibly abuse being attempted. Again, this trick isn't 100% reliable because user agents can see any string they want as their user agent string.
Use post method instead of get.Read the documentation here http://api.jquery.com/jQuery.post/ to learn how to use post method in jquery
You could, for example, implement a check if the request is really done with AJAX, and not by just calling the URL.
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
// Yay, it is ajax!
} else {
// no AJAX, man..
}
This solution may need more reflexion but might do the trick
You could use tokens as stated in Slicedpan's answer. When serving your page, you would generate uuids for each images and store them in session / database.
Then serve your html as
<img src='src.jpg' data-id='42' data-uuid='uuidgenerated'/>
Your ajax request would become
$.ajax("action.php?action=click&uuid=uuidgenerated");
Then on php side, check for the uuid in your memory/database, and allow or not the transaction. (You can also check for custom headers sent on ajax as stated in other responses)
You would also need to purge uuids, on token lifetime, on window unload, on session expired...
This method won't allow you to know if the request comes from an xhr but you'll be able to limit their number.

How to manage multiple tabs with PHP

I have an application that uses several different forms to perform various actions. Some of the forms access data from submissions in other forms.
For example: a user places a new order on one form and adds a new item in another, the items can be added to orders.
So this leads to the possibility that a user may be adding an order and realize that an item must be added first. So naturally a new tab will be opened to do so instead of losing the information added to the order.
Currently I have a $_SESSION['form'] variable to let the form handling script know which function to use after form submission. The problem is that with multiple tabs open, this value will get overwritten by the last opened tab. I have had a couple ideas on how to handle this but so far nothing ideal.
Idea one: use a hash value to identify different page loads and send the hash as a hidden field
$hash = $_POST['hash'];
$form = $_SESSION[$hash]['form'];
Issue: session overloading. This method will make a new session value each time a form is loaded to uniquely identify the form submission. I could unset the value upon submission, but what if a form page is loaded and never submit, or if the page is refreshed. I would prefer to keep the session as light weight as possible.
Idea two: use AJAX to set the $_SESSION['form'] value upon clicking submit
Issue: users that do not use JS. I would like to be able to continue providing support to users that prefer to disable JS if possible, although this method seems like it could be a bit better. However, I am unsure whether there could be browser compatibility issues here.
Idea three: create a hash id for each window
Issue: PHP can't distinguish between browser tabs. This would be the most ideal by far.
Idea four: split the form handling script into multiple files, thus removing the need for a value to select which function to use.
Issue: inconvenient, but I am open to this idea if it proves to be the only real method to handle this issue. It would require a fair bit of re-structuring though.
Any ideas on how to securely manage different tabs and session information in PHP?
This is just my opinion, but I would use idea #1-- each time the form loads, give it a random hash (just md5 of the time() + random number would suffice), and then keep up with those hashes like they are unique window identifiers.
Yes, it might bloat your session a little, but if it doesn't slow down the user's experience, I wouldn't worry about it. If it really bothers you, you can keep track of how many window ID's you've created for the user (in the session) and limit it to 100 or 1000. This would prevent a malicious user from writing a script to open millions of windows.
Remember, when the user's session is destroyed (by logging out/closing the browser), all those hashes are gone too. And if you were REALLY feeling industrious, you could have a system of expiration on hashes. Like, after 2 hours they will be removed from the session.
But honestly-- if it were me, I'd just leave them in the session and not worry about it.
Just my 2 cents,
Richard

How to protect from malicious use of jQuery post handler?

I use jquery POST calls to fetch data to display in various sections of my websites.
Usually, they POST to a single 'ajax_handler.php' page which reads the requested parameters and returns the relevant data. Typical parameters might be 'get_order_details',123
How could I stop users posting to the script to try and retrieve data which they should not be able to? I know I can verify that data belongs to the currently logged in user, for instance, but how could I stop users 'guessing' that there might be a handler for 'get_some_info'?
Since users could even run javascript straight from the URL this seems to be a major security problem to me as the client would have access to SESSION and COOKIE data (which I would otherwise use for security).
I guess I could start by naming each of my handler identifiers with a random string, but I'd prefer not to compromise the legibility of my code.
Naming your handlers with a random string is security through obscurity and while it might slow someone down, it won't stop them.
The best thing to do is to store a session or database checksum each time a page is accessed. Then also send that same checksum along with the POST content. When the form is submitted, through AJAX or otherwise, you compare the checksums. If they don't match then you know the user wasn't on the appropriate page and is trying to access the handler through some other method.
For each user, you can store within your database which data he should be able to view and which he shouldn't. Each time you get a request, e.g get_order_details, you should call a function which does your security checking to make sure both that the user is logged in, and that he has access to the 'get_order_details' method or any other method he is trying to access.
What you're trying to do is fundamentally antithetical to how the Internet works. You can't and shouldn't attempt to limit the way users make requests to your services. This is an extremely outdated and backwards way of thinking. Instead of trying to limit the ways in which users can use your service, be thankful that they're using your service in the first place.
All you can do is make sure that the user is authenticated and has access to the record they're requesting. If you're using a system which has no authentication, and you want to prevent users from "guessing" the ID of the next record, don't use sequential IDs. Use randomly generated strings as your identifier. Make them sufficiently long that it will be difficult for users to stumble upon other records.

Is it safer to transfer previous page location via GET method rather than HTTP_REFERER?

I want to store the page location the user came from (on my site). I want to do that for this example: say someone sent a comment without being logged in. "process_comment.php" will process it and send a header(location:$_GET['prev_page']); Of course I'm gonna filter $_GET before sending it.
Should I use a session instead?
Thanks!
It is actually exactly the same. Both methods imply that the information is passed in the HTTP query, which can easily be forged. So you can't really trust one method more than the other.
That being said, as long as you don't rely on that information for something really important, you can admit that the referer can be trusted, because it's a little bit more complex to forge than a querystring parameter. At least for the average user.
The best solution, if you need to trust that information for something important, would be to store it on the server, as a session variable for instance. Each page would store its URL, after checking what the previous value was.
If you use $_SESSION, there will be trouble if the user has multiple windows/tabs open and does different things at once. There is nothing more annoying than being able to only have window of a site.
You could store the value in a SESSION variable and identify it by a short key. That key goes into the GET string. That way, you can keep your URLs clean, and you don't risk hitting the 1024 byte limit many servers have for GET parameters.
Well, the HTTP_REFERER can be stripped out by some clients.. I seem to remember some Norton Internet security products did that, probably others do too. So it is going to be more reliable for you to set the previous page in a session and use that for redirecting.
If you can use it, session is a safer option. Sending user back from GET or even headers will allow crafty people to possibly abuse any flaws in your code to possibly do nasty things.
The header itself may also be removed by some firewall software.
I don't think there is a problem with using GET in this case. You can't always depend on being able to retrieve the referrer from the browser.

How to use sessions in place of a querystring

Using PHP.. I have a small app that I built that currently uses a querystring to navigate and grab data from a database. The key in the database is also in the string and that is not acceptable anymore. I need to change it. I would like to hide the db key and use a session in place of it but I'm not sure how to do that. In fact, there are also other variables in the query string that I would like to use sessions for if at all possible.
page.php?var1&var2&id=1
This is what my string looks like. I am looping through the results in the database and have given each row the id so that when the user clicks the row they want, but I'm not sure how I could do this with a session.
Does anyone have any ideas?
Thanks
EDIT:
I'm developing an email type system where senders and recipients are getting and sending mail. Each piece of mail that is stored on the server will have its own unique key. Currently, I am using that number to retreive the message but the problem is that I don't want people to change the number and read other people's mail. I can probably use a GUID for this or even some sort of hash but I really hate long query strings. I was just thinking it would be so much cleaner if there was a way to "hide" the id all together.
UPDATED (Again ... Yeah, I know.)
Allowing access to a particular set of data through a $_GET parameter is much more accessible to any user that happens to be using the application.
UPDATED
For storing a private record key, you are probably going to want to use post data, and if you really want it to look like a link, you can always use CSS for that part.
Honestly, the best way to stop people from reading other people's mail is by having a relationship table that says only X person is able to access Y email (by id). That or have a field that says who is the 'owner' of the email.
The fact is that users can still get access to POST parameters, and can easily forge their own POST parameters. This means that anyone could realistically access anyone else's email if they knew the naming scheme.
In an ideal system, there would be a Sender, and a Recipient (The Recipient could be comma separated values). Only the people that are on one of those columns should be allowed to access the email.
How To Use Sessions (From Earlier)
First start off with calling session_start(), and then after that check for variables from previous scripts. If they aren't present, generate them. If they are, grab them and use them.
session_start();
if(!isset($_SESSION['db_key']))
{
$_SESSION['db_key'] = // generate your database key
}
else
{
$db_key = $_SESSION['db_key'];
}
Sessions are stored in the $_SESSION array. Whenever you want to use $_SESSION, you need to call session_start() FIRST and then you can assign or grab anything you like from it.
When you want to destroy the data, call session_destroy();
Also check out php.net's section on Sessions
Your question isn't too clear to me, but I understand it like this:
You need some variables to decide what is being displayed on the page. These variables are being passed in the URL. So far so good, perfectly normal. Now you want to hide these variables and save them in the session?
Consider this: Right now, every page has a unique URL.
http://mysite.com/page?var1=x&var2=y
displays a unique page. Whenever you visit the above URL, you'll get the same page.
What you're asking for, if I understand correctly, is to use one URL like
http://mysite.com/page
without variables, yet still get different pages?
That's certainly possible, but that means you'll need to keep track of what the user is doing on the server. I.e. "user clicked on 'Next Page', the last time I saw him he was on page X, so he should now be on page Y, so when he's requesting the site the next time, I'll show him page Y."
That's a lot of work to do, and things can get awkward quickly if the user starts to use the back button. I don't think this is a good idea.
If you need to take sensitive information out of the URL, obfuscate them somehow (hashes) or use alternative values that don't have any meaning by themselves.
It completely depends on your application of course, if the user is accumulating data over several pages, Sessions are the way to go obviously. Can you be a bit more descriptive on what your app is doing?
Edit:
but the problem is that I don't want people to change the number and read other people's mail
If your primary concern is security, that's the wrong way to do it anyway. Security through obscurity is not gonna work. You need to explicitly check if a user is allowed to see a certain piece of info before displaying it to him, not just relying on him not guessing the right id.
There are some examples on how to use $_SESSION on php.
Registering a variable with $_SESSION
The issue with using sessions for using it in place of S$_GET or $_POST is that you need some way to read the user's input so that you can store it in the session, and you need a way to trigger a page refresh. Traditional means is via hyperlinks, which defaults to GET (unless you use Javascript) or forms, which defaults to POST.
Maybe ajax will help you here. Once the user has enter info into a form or a checkbox, use JS to send a request to insert the info to the PHP and send info back, whether it is to refresh the page or to fill a with content.
Hope this helps

Categories