Website security question with PHP? (Probably applies to ASP/Rails/etc.. too) - php

Say, I have "index.htm" and "routines.php".
"index.htm" will call eventually call "routines.php" using JS (AJAX).
So, my question is, how can "routines.php" verify that the request came from the same local server and not outside? Is there a global variable I can check at PHP level or HTTP level?
Edit 1: Using AJAX

You may forget about the Ajax part as it's not really part of the problem. You should read about Cross Site Request Forgeries (CSRF) and CSRF tokens. Some links:
http://en.wikipedia.org/wiki/Cross-site_request_forgery
http://shiflett.org/articles/cross-site-request-forgeries

To answer your question with another question: how would you invoke getdata() using a browser?
(So: no need to worry.)

If the call is made in JavaScript (i.e., on the client), you really can't do anything to definitely prevent someone from simulating a request from index.htm, even if you check the Referer (sic) header.
If the request is made on the server side, you could use some kind of key.
You can of course generate a key on the client side too, but this security measure can be imitated by anyone to simulate a request from index.htm.

you could use a session key:
index.htm
<?php
$_SESSION['safe_key'] = true;
?>
javascript code here
routines.php
<?php
if (!isset($_SESSION['safe_key'])) {
die('from outside');
}
function getdata() { ... }
?>
Basically what happens is when index.htm is called a session safe key is created. Sessions are serverside only. In routines.php if the safe key does not exist, the code was not called from index.htm. If it does exist, run code.

As others pointed out, it would be pretty difficult given your original specification. However, if you can change the index.htm to be index.php and have it output the same content as index.htm, you can also put in additional tokens for session management (e.g. Cookies - yes I know they are easy to spoof too :) and reject the call to getdata() if the tokens don't match.

Use the HTTP_REFERER server variable:
echo $_SERVER['HTTP_REFERER']
With this you can know if the request comes from the server you want.

Related

How have safe HTTP Request Method

when use GET Method for receive JSON data , we can acsses the result directly from web browser , for example i send a mydata value from ajax to a main.php file and it process and get answer show a result some thing like below :
<?php
if (isset($_GET["mydata"])) {
if ($_GET["mydata"]=="hello"){
echo "hello world";
}
}
?>
but when a user call it in browser directly like http:mysite.com/mydata.php?mydata=hello recive answer . i want dont allow users to get answer of http request directly , and just can show it from ajax result of main page is it possible ?
You're asking how to prevent an ajax-only request from being accessed directly by copy-pasting the URL into the web browser; that is, only allowing the URL to be accessible via ajax on the main web page.
Well, there are a few things you can try:
Check the Referrer for the URL of the main page with $_SERVER['HTTP_REFERER']
Set a header in Javascript using xhr.setRequestHeader() and then ensure it's value by checking for $_SERVER['HTTP_X_....'] in PHP
Like Jay Bhatt recommended, check for the X_REQUESTED_WITH header, but be aware this might not always be set (see: X-Requested-With header not set in jquery ajaxForm plugin)
However, in any of these situations you should be aware that anyone who knows what they are doing can easily set any HTTP header, variable, or even modify the referrer which is sent to the server. As such, there is no 100% guarantee that your resouce can be accessed only via AJAX on the main web page. There is no control built in the internet to verify where a request is coming from, so anyone can easily spoof or fake it.

Superglobals: PHP security

Is it possible for a client to modify PHP superglobal variables, especially $_SERVER, somehow - maybe not in a common way?
In other words, is this code secure:
if (($this->error->getCode()) == '404') {
ob_clean();
echo #file_get_contents("http://".$_SERVER['SERVER_NAME'].'/404.html');
}
This code is fine - SERVER_NAME can't be modified. The ones to be careful with are $_SERVER['PHP_SELF'] or $_SERVER['REQUEST_URI'], as a user could add some js to the address bar - if these are written out to the screen they should be carefully escaped.
Your code is fine though.
Yes, that's fine.
No user can change any variable of your code unless you leave it open to them using some sort of POST/GET/COOKIE etc
On a side note, if the file is on your server, why are you using file_get_contents()?
In this case, since the $_SERVER variable only contains data related to the web server that the script is being executed on, I don't see any potential security issues unless the web server itself has been compromised. In that case, you've got a lot bigger problem on your hands. The main exception to this rule is if you use PHP_SELF or REQUEST_URI since those values can be altered via user input in the URL bar.

Detect if PHP file directly loaded

I'm using some PHP pages do some AJAX stuff but I don't want them to be directly accessible. Facebook does a similar thing so for example: domain.com/ajax/my_ajax_form.php
If I was to load that page using AJAX it would work fine, but if a user were to try and loading the file directly by typing in that url it would do through an error so e.g.
if( IS FILE LOADED DIRECT? )
{
header ( HTTP/1.0 404 );
}
This isn't possible. You cannot rely on $_SERVER['HTTP_X_REQUESTED_WITH'], and even if you could, it doesn't matter. Anyone can send the same request to your server that your browser does, via POST or GET.
What you should do is validate the request, and return the proper result if it is valid. If it is invalid, do not return a 404. (Browsers can cache errors like 404. If your client-side code had a trouble, subsequent requests may fail!) If the request is invalid, return an error of some sort.
Again, it is impossible to secure stuff like this. You should be validating the session and request data. That's all.
You can look for the HTTP_X_REQUESTED_WITH header.
$is_ajax = array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER)
&& $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
if (! $is_ajax) {
die('go away.');
}
Note, though, that it's not standard, but needs to be set explicitly on the requesting side. AFAIK, at least jQuery and Mootools set it though, probably most others as well, but don't take my word for it.
Simplest way is to only access that page via POST, and not via GET. Though keep in mind - if a browser can do it - then a hacker can too.
You have to use session variables, or more generally, cookies.
With cookies: (set in JavaScript)
JavaScript: Set token in cookie
JavaScript: Make XMLHttpRequest
Server side: Check token from cookie
Server side: Return JSON output or error message
Please note that this is no way secure! This just prevents easy linking.
With session variables: (cookies set in server side)
Server side: Authenticate user, set privileges
JavaScript: Make XMLHttpRequest
Server side: Check privileges
Server side: Return JSON output or error message
This method is as secure as the user authentication is.

Does $_SERVER['HTTP_X_REQUESTED_WITH'] exist in PHP or not?

All over the Internet, included even here at Stack Overflow, people state that a good way to check if a request is AJAX or not is to do the following:
if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) {...}
However, I don't see $_SERVER['HTTP_X_REQUESTED_WITH'] in the official PHP documentation
And when I try to do the following:
echo $_SERVER['HTTP_X_REQUESTED_WITH'];
Nothing is outputted.
Am I doing something wrong? Because I'd really like to be able to use $_SERVER['HTTP_X_REQUESTED_WITH'] if it's available.
The variables in $_SERVER are not really part of PHP, which is why you won't find them in the PHP documentation. They are prepared by the Web server which passes them on to the scripting language.
As far as I know, the X-Requested-With is sent by the Ajax functions of most major Frameworks but not all (Dojo, for example, added it only two years ago: #5801). As such, and taking into considerations #bobince' comments, it's safe to say it's not generally a 100% reliable method to determine whether a request is an AJAX request or not.
The only 100% secure way is to send a pre-defined flag (e.g. a GET variable) along with the request and for the receiving page to check for the presence of that flag.
don't forget that you can easily spoof any header with cURL like so
curl_setopt($ch,CURLOPT_HTTPHEADER,array("X-Requested-With : XMLHttpRequest"));
$_SERVER keys that start with HTTP_ are generated from HTTP request headers. In this case, the X-Requested-With header.
This header is a standardization-in-progress from all of the AJAX libraries out there.
It won't be documented in the php documentation per-se, but rather in the different AJAX libraries that set this header. Common libraries do sent this header: jQuery, Mojo, Prototype, ...
Usually these library will set the header using
xhrobj.setRequestHeader("X-Requested-With", "XMLHttpRequest");
Here's a quick function with example usage:
function isXmlHttpRequest()
{
$header = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? $_SERVER['HTTP_X_REQUESTED_WITH'] : null;
return ($header === 'XMLHttpRequest');
}
// example - checking our active call
if(!isXmlHttpRequest())
{
echo 'Not an ajax request';
}
else
{
echo 'is an ajax request';
}
echo $_SERVER['HTTP_X_REQUESTED_WITH'];
What'd you expect from such a code? Assume you're running it directly from the browser, not using AJAX request. So, how come this header could be set?
Well the Answer to the Ultimate Question of Life, the Universe, and Everything - an HTTP sniffer! Get yourself one and forget of printing $_SERVER variable.
Firebug has one, or you may want to use Fiddler HTTP proxy or LiveHTTPHeaders Mozilla plugin. I'm bored to make links but it easily googled.
So, with HTTP sniffer you can be sure of any HTTP header ever.
Note that you can't prevent any "direct access" by using XHR, as every HTTP request to your server is already "direct".
You have to set it specifically in your ajax request object (that is if you are not using a framework like jQuery), but core Javascript; like so:
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
Where xhr is your request object.
Then, PHP will now receive and set it in the global variable $_SERVER like so:
$_SERVER['HTTP_X_REQUESTED_WITH']
Otherwise $_SERVER['HTTP_X_REQUESTED_WITH'] will always be null.
Note: In your javascript, Please make sure you set headers after the request is open. I mean after xhr.open() method.
You can also blame some browser bugs - see this question and its solution for Firefox
Firefox does not preserve custom headers during Ajax request redirect: an ASP.NET MVC solution
IE also having caching issue which is more serious then detection of request method.
You anyway needs to add cache busters to avoid caching, so why not use another flag to specify the ajax call - or more better you can use different URL like http://ajax.mysite.com/endpoint/sevice?params
I agree Pekka. There is no reliable native method between front side and back side that can auto-detect if a client is really calling an endpoint using AJAX.
For my own use, I have few main ways to check if a client is requesting one of my endpoint:
I can use HTTP_X_REQUESTED_WITH when I'm not in cross domain context.
instead of checking "X-requested-with", I'm checking $_SERVER['HTTP_ORIGIN'] (that is sent from AJAX request) intending to handle cross domain permissions. Most of time, the main reason why I'm checking if a request is an AJAX request, is especially because of cross domain permissions, using this PHP code: header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']); // If this "HTTP_ORIGIN" is in my white list
my APIs expect from the client to explicit, in few cases, the datatype (JSON, HTML etc.) into a GET or a POST var. For example, I check if $_REQUEST['ajax'] is not empty or equal to an expected value.
The best solution to make sure if an HTTP request is truly sent via AJAX is using SESSION checking , you send session_id in a get parameter and you check this session if it's allowed or not !
$headers = apache_request_headers();
$is_ajax = (isset($headers['X-Requested-With']) && $headers['X-Requested-With'] == 'XMLHttpRequest');

how to prevent PHP's file_get_contents( )

one of my php page returns data like this:
<?php
//...
echo "json string";
?>
but someone else use file_get_contents() to get my data and use in other website.
can anybody tell me what can i do to prevent such thing happen.
i consider if i can get the request's domain name to echo something else.but i dont know
the function to get request's domain name.and if the request is sent by a server,that
will be unhelpful. My English is poor, to express doubts, please bear with.
you can also use sessions. if somewhere in your application, before the user gets the json data, you start a session, then in this page where you are outputting json data, you can check for the session variable. this way only users that have passed the session generator page, can view your output.
suppose you have page A.php that generates the session. use this code before outputting anything in this page.
session_start();
$_SESSION['approvedForJson'] = true;
then in your page where you are outputting json data, before outputting anything, call session_start() again. the beginning of your PHP code is a good place to call it.
then before outputting the json data, check if the session variable for approved users exists, or not.
if ( isset($_SESSION['approvedForJson']) && $_SESSION['approvedForJson'] ) {
echo "json data";
} else {
// bad request
}
You can use $_SERVER['REMOTE_ADDR'] to get the address of the client address. You can also check $_SERVER['HTTP_REFERER'] and block external requests that way, but it's less reliable. There's probably a few other techniques involving $_SERVER that you can try.
Your fighting an uphill battle here. I am assuming your serverside process that responds in json is being consumed via javascript in your users browsers... so there is no easy way to encrypt it. You might try some of the techniques used to prevent xspf (see http://en.wikipedia.org/wiki/Cross-site_request_forgery ). If you developed the client to pass along some session token that is uniq per client you could reduce some of the problem. But, chances are whoever is stealing your data is gonna figure out whatever mechanism you put in place ... assuming this is some sort of ajax type thing. If its a server-server thing then as sli mentions, setting up some restrictions based on the remote ip would help, plus setting up some sort of API authentication tokens would help even more (see oauth for some pointers)
You could also using .htaccess with apache block every external request to the page if it get's called internally or block every request that is not from your domain:
Google search thingie
EDIT
You could also use some php file which includes the file which can not be read. So for example you have file.php:
<?php
$allowedFiles[] = 'somefile.php';
$allowedFiles[] = 'someotherFile.php';
$allowedFiles[] = 'jsonReturnFile.php';
if(in_array($_GET['file'], $allowedFiles)){
include( "include/".$_GET['file'] );
}
?>
Then you can allow file_ get _contents() on that file and write a rewriteRule in your .htacces to disallow any request to the include/ folder.
RewriteRule include* - [F,NC]
That will return a 403 forbidden error for a request to that directory or any file in the directory.
Then you can do you JSON request to something like: file.php?file=jsonReturnFile.php&someothherParamReadByJsonFile=1
And when someone tries to get the file contents for the JSON file they will get the forbidden error, and getting the file contents for the include.php won't return anything usefull.

Categories