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.
Related
I wanted to pass a hidden variable at first with $_POST but i have come to realize that users can change $_POST almost as easily as $_GET. Is it possible to somehow restrict this ability or is there another way to do this? Also, it doesnt seem you can use $_POST in this simple example below?:
index.php
<view recipe
test.php
$variable = $_GET['variable'];
$query = $database->query("SELECT name, description, date_added from recipe where recipe_id = $variable");
(EDIT: i do check that the input is indeed an integer although i skipped this above to minimize the code of the example. I should've made this clear earlier).
I guess the only borderline "malicious" things a user could do here is loop through the recipe_id:s to find out how many recipes were in the database or even the first recipe added just by changing the $variable. Not that i care in this particular case, but im sure i will when it comes to other examples. I realize that i want to make the information available, i just want it to go through the "proper channels" i guess. Is this just the way things are or am i missing something vital?
People always write things like "validate your input". And i agree with that. But in this case its just one integer the user "inputs". What can be done besides that validation? Again, im slowly progressing/learning this so please be patient if i seem to make simple mistakes.
(Using PHP, PDO, Mysql)
Thank you!
It's all about HTTP requests. The browser communicates with the server through HTTP requests. These are entirely transparent to the user however you look at it. Open the Web Inspector in your browser (or Firebug, or Fiddler or whatever else) and inspect the raw HTTP requests, live. Anyone can send these requests to your server anytime, containing any data at all.
Understand this concept, it is important. There's no such thing as "secret" information in the communication between your server and the client (from the POV of the client). You do not control the input to your server. Neither can you "hide" any data that is going from the client to your server.
An HTTP request represents a, well, request for data or for some action. The server should, well, serve the request to the best of its abilities. If a request is invalid, the server must reject it. The server should judge the validity of each request independently of every other request, it should be stateless. The server cannot presume the validity of any request without validating it. It should only ever output information which is not security sensitive and treat all incoming data as suspicious. That's the fact of life.
The "variable" isn't restricted to be an integer at all in your code. An evil user could probably change the value of "variable" to "';truncate recipe;--". Execute this and whoops ... all recipes are gone.
always ensure that you use proper validation. search the interwebs for "sql injection" and have a look at functions like mysql_real_escape_string (and it's documentation).
If you don't want users to see an incremental recipe ID that they can easily modify, then generate a unique random string to use to identify recipes. This will stop users from being able to play with the recipe ID in the GET or POST data.
They can still modify it but they will need to obtain the random string in order to pull out a recipe.
The content of request fields, whether $_POST or $_GET, is not typed. It is just plain strings, which means that it's pretty much "open game" on the client side. It's the very reason we keep repeating that client input cannot be trusted, and must be validated on the server side.
Regarding your second question: recall that $_GET will contain the result of a form using the get method, while $_POST will contain data from post method forms. $_GET purpose is to contain url parameters, if they exist (to be precise, a get method form will pass all its parameters via the url).
On a side note, I should also tell you that you shouldn't use one verb for another, each one as a specific purpose. The get method is about getting data, not updating it, while post, put, delete are about updating data. This means that your example is following these rules, and should not try to use a post form instead (although this is technically feasible, you would just need to replace your link tags with forms).
see the HTTP specs on this matter.
you may create a session based permission system that:
when user visited your site, and his/her browser rendered the link, in that php, you set a session variable for that link,
$_SESSION["permitted_recipe_id"] = $row['recipe_id'];
Now you know exacly what recipe id that user can click,
with this check:
$variable = $_GET['variable'];
if($variable != #$_SESSION["permitted_recipe_id"]){
exit("error you do not access to this result");
}
$query = $database->query("SELECT name, description, date_added from recipe where recipe_id = $variable");
This will ensure that user visited the page before it send a request. This solution will prevent consecutive POST requests to fetch all website data,
if you show multiple links that user can click, you must implement $_SESSION["permitted_recipe_id"] as an array containing all ids' that send to user as links.
but beware, multi tab experiences may create bugs for this solution. This is the idea, you have to carefully work it out for a relase enviroment solution.
In the past when I have written HTML with jQuery, in order to access specific PHP pages I have simply always done this:
user.php?Action=1&User=Adrian.....
And this would return plain text JSON, which jQuery converts into a javascript object. I have a few questions regarding this method, though.
Is this safe? JSON is being passed back in plain text. Should it be used over HTTPS?
How is the best way to prevent direct access to PHP? Simply checking for an active session?
Is this whole approach ok?
Cheers,
Adrian
For number 2, it depends what you're doing.
If you are doing anything with the 'user.php' file to make any changes to the DB, you would want to use POST rather than GET (this hides the parameters from the URL bar, and is safe if your page is getting crawled/scraped).
To use POST, in your user.php file replace instances of $_GET with $_POST.
In your jQuery Ajax call, make sure parameter "type" is set to "POST"
type: "POST",
GET requests should only ever be for doing anything that gets and displays data from your data model (DB, whatever). POST requests are for making any updates, additions (stricter would be PUT), or deletions (DELETE).
If you want only that specific user to access the user.php script, then you would want check the user's session and make sure it matches with the user trying to access the particular user parameter.
1) It depends on what you're transferring. If you're transferring credit card data via HTTPS, definitely. If you're transferring less sensitive data, maybe not.
2 and 3) Make sure you check your input for SQL injection, just as you would with any $_GET variable. You should really treat it how you would normally treat $_GET data
Whenever I've allowed JSON/crud access, I've always secured the PHP page serving it (user.php) behind session-based access-control rules (role-based or group-based permissions). I then white-list which tables/fields that user/group/role is allowed to access. The level of sophistication depends on the nature of the data that is being served.
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.
When the use of GET method is justified? Is it OK to always use POST? Is it right that GET is transferred fast then POST?
The GET verb is used in Requests that are idempotent, e.g. when they lead to the same result (and have no observable side-effects on the returned resource). So, you use them for retrieval of a resource only.
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response and not the source text of the process, unless that text happens to be the output of the process.
and also
1.3 Quick Checklist for Choosing HTTP GET or POST
Use GET if:
The interaction is more like a question (i.e., it is a safe operation such as a query, read operation, or lookup).
Use POST if:
The interaction is more like an order, or
The interaction changes the state of the resource in a way that the user would perceive (e.g., a subscription to a service), or
The user be held accountable for the results of the interaction.
However, before the final decision to use HTTP GET or POST, please also consider considerations for sensitive data and practical considerations.
See
http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html and
http://www.w3.org/2001/tag/doc/whenToUseGet-20040321
That second link explains the difference pretty well.
Note that there is not just GET and POST in the HTTP protocol, but a couple of other verbs, like PUT, HEAD, DELETE, etc as well. Those play a large role in RESTful applications.
Short answer:
Use GET requests when it makes sense for the user to be able bookmark the request, share the request, and come back to over and over again. It makes sense to be able to bookmark the result of a Google query, for example.
Longer answer:
Use GET requests when the user is simply fetching/viewing a resource, and doesn't have any significant side-effects on your website's data or on future requests. If the request is creating, modifying, or deleting something, it should be a POST. If the user is logging in to a website, that has effects on future requests, so it should be a POST, not a GET.
Note: Users can still change POST variables.
It's easier to for the user to change query string (GET) values, but it's not too difficult for the user to change POST values. Your website's security should take this into account! Using POST for security isn't really a valid reason, except for the fact that POST variables aren't part of the URL and aren't bookmarked, while GET variables are. This prevents users from accidentally sharing things like passwords when sharing links.
You use post for larger amounts of data, or data that you don't want to appear within the url. For instance, you don't want the url to delete a page, or create one, to appear in someones history. Neither do you want to save passwords in this way.
For search strings and such, you can easily use get. It allows users to copy a specific url, like a specific search reasult, or a link to the 5th page in a paginated list.
So, either are ok for their own purposes. The only thing you should remember is the maximum size of 8Kb for an url, including the get parameters.
GET is better for things that should be able to be bookmarked, and simple queries with few, short parameters.
POST is better for sensitive fields that the user shouldn't see, for large binary transfers, and for transfers with many fields or very long fields.
Generally, GET is preferred for search pages (like on google) and something which is not sensitive like username or password should not be shown in urls.
Consider Security Too:
You should be very selective when using GET for example if you end up coding like this:
$page = $_GET['page'];
include $page . '.php';
A bad guy can visit a url like:
http://www.yourdomain.com?page=../../../etc.pwd
Or if you do:
$id = $_GET['id'];
mysql_query('delete from table where id = $id');
A bad guy can delete all your records from db just by visiting:
http://www.yourdomain.com?id=5
http://www.yourdomain.com?id=10
There do exists solution to those mistakes however but still you should be selective when choosing between POST and GET.
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