6

How can I detect if my php script is being called from another domain and the other domain is making illegal use of my script? Is there a way to prevent this too?

UPDATE

I found this question on SO, but its still not safe, it can be spoofed.

Community
  • 1
  • 1
Sujit Agarwal
  • 12,348
  • 11
  • 48
  • 79
  • Using an access token is probably your best bet. Referers aren't reliable and easily forged. – Marc B Jun 14 '11 at 18:02
  • Oh, but i dont know how to implement a access token. Could you please show some light on it through an answer? – Sujit Agarwal Jun 14 '11 at 18:05
  • 2
    Set a cookie of some sort of authorized users of the service. Check for this cookie on each invocation of the ajax handling script. Or embed the token in your script so its sent as a parameter on each AJAX call from your own legitimate code. Basically have SOMETHING sent along with the request to identify a legit user from one of the bandwidth thieves. If they get smart and copy the token, then only provide the token via a login mechanism or some other per-user identification system. – Marc B Jun 14 '11 at 18:08
  • possible duplicate of [How server can check ajax requests not from site, X-Requested-With](http://stackoverflow.com/questions/4929432/how-server-can-check-ajax-requests-not-from-site-x-requested-with) – phihag Jun 14 '11 at 18:15

6 Answers6

4

There isn't any absolutely foolproof method to prevent this, since any header information can be spoofed. Session-based tokens are another possible solution, but in that case your javascript is publicly accessible, so anyone who wanted to spend a little time could determine how your token system works and figure out a way around it.

A combination of methods will give you the most wide-ranging protection. You can look for the header, use and .htaccess file, and use tokens. This sort of all-of-the-above approach makes it that much harder to abuse a web server - most abuse comes from people trying to find an easy hole to exploit. The important thing to remember is that you can't become complacent because you've deployed "the best" protection, or because you've got so many layers of protection that it seems impossible to crack. If someone really wanted it bad enough and had the time, they'll find a way. These types of preventative measures are really only deterrents to keep away the lazy, curious, and idly malicious. Targeted attacks are a whole separate class of security, and usually are more centered on server-level security issues.

Sample htaccess. This would not be something you'd put in your root, but rather within a subfolder where you have scripts that should never be called from the address bar:

RewriteEngine on
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?_YOUR_DOMAIN_NAME_HERE.com [NC]
RewriteRule \.(php)$ - [NC,F,L]

Check out this article for info about using a token system: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

Chris Baker
  • 49,926
  • 12
  • 96
  • 115
1

You can manually deny every request whose Origin header does not match your domain name. However, not all browsers send the Origin header. In these cases, you can fallback to the Referer[sic] header, parse it and find out the domain name, and compare it as above.

Some JavaScript frameworks also set an X-Requested-With header for AJAX requests.

This should reject a significant percentage of users (I'd estimate >95%). Note that due to the Same-Origin Policy, the only thing the guy sending AJAX requests to your domain gets is timing information anyway.

phihag
  • 278,196
  • 72
  • 453
  • 469
  • Check the update. BTW, in your answer's case, if all browsers dont send the Origin header, then what else can be done? – Sujit Agarwal Jun 14 '11 at 18:04
  • ` the only thing the guy sending AJAX requests to your domain gets is timing information anyway.` what do you mean by this? – Sujit Agarwal Jun 14 '11 at 18:07
  • @Coding Freak Updated with a detailled explanation and `X-Requested-With`. However, I've no idea why someone would send random AJAX requests. – phihag Jun 14 '11 at 18:07
  • Because recently i have been checking out in my logs that many XHR requests have been made from an outside server. – Sujit Agarwal Jun 14 '11 at 18:08
  • @Coding Freak The Same-Origin Policy prevents you from getting the results of an AJAX call to another domain, unless you explicitly opt in to Cross-Origin Resource Sharing. Therefore, I can't imagine the motivation somebody would have to send AJAX queries to your site. – phihag Jun 14 '11 at 18:09
  • @Coding Freak What do you mean with "made from an outside server"? XHR requests are usually made by the *client*. The Referer points to another site or what? Can you provide an example log entry? – phihag Jun 14 '11 at 18:10
  • Sorry, a bit mistake, I didnt know, that my web server is enabled for Cross-Origin Resource sharing or not, but i have been observing a huge consumption of bandwidth on my server without that number of reasonable visits on my pages. – Sujit Agarwal Jun 14 '11 at 18:13
0

A bit like user3491125 proposed, you could set a $_SESSION in the page where the call is made and check it on the page Ajax called if there is for instance $_SESSION['user'] set.

yemd
  • 420
  • 4
  • 15
0

for user3491125's answer, you could try encrypting the session token. I have one encryption function that can add a unique key based on a users port 80 IP. it's Not foolproof, but it does make it more difficult for hackers.

function encryptString($string, $action, $baseIP = 'false', $extraKey = ''){
    global $flag;

    $encryptedIP = '';

    if($baseIP){
        $encryptedIP = encryptString(strip_tags(htmlentities($_SERVER['REMOTE_ADDR'])), 'encrypt', false);
    }

    $output = false;

    $encrypt_method = "AES-256-CBC";
    $secret_key = $flag['encrypt-key'].$encryptedIP.'-'.$extraKey;
    $secret_iv = $flag['encrypt-secret'].$encryptedIP.'-'.$extraKey;

    $key = hash('sha256', $secret_key);
    $iv = substr(hash('sha256', $secret_iv), 0, 16);

    $output;

    if($action == 'encrypt'){
        $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
        $output = base64_encode($output);
        $output = str_replace('=', '[equal]', $output);
    }else if($action == 'decrypt'){
        $setString = str_replace('[equal]', '=', $string);
        $output = openssl_decrypt(base64_decode($setString), $encrypt_method, $key, 0, $iv);
    }

    return $output;
}
SwiftNinjaPro
  • 787
  • 8
  • 17
-3

I know this is an old post but currently THERE IS a "foolproof" method to avoid this and its as simple as hell... First in the page where the ajax call will be made:

<?php
$token = sha1(rand(1000,9999)); 
$_SESSION['token'] = $token;
?>

Then in the ajax script

var formData = new FormData();
formData.append("token","<?php echo $token;?>");
        $.ajax({
            url: 'yourfile.php',
            type: 'POST',
            xhr: function() {
                var myXhr = $.ajaxSettings.xhr();
                return myXhr; 
            },
            success:function(data) {
                alert(data);
            },
            data: formData,
            cache: false,
            contentType: false,
            processData: false
        }); 

And finally in the php page that will be called:

<?php
$token = $_POST['token'];
if($token === $_SESSION['token'] && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
{
   //Perform your stuff here...
}
?>    
user3491125
  • 366
  • 1
  • 3
  • 13
  • 1
    The problem here is rand() is not really random. It's a time based pseudo-random number. While this is still a preferred method, it's by no means "foolproof". – Chris Fremgen May 07 '15 at 21:31
  • but it has no impact since the string is stored on a session so only associated to this computer ;) thanks for informing anyway – user3491125 Jun 03 '15 at 15:03
  • I think the answer on [session spoofing](http://stackoverflow.com/questions/17413480/session-spoofing-php) helps with this in some ways but not sure if this is the ideal solution OP was looking for. – aug Jan 12 '16 at 00:15
  • This is also not foolproof because the token is exposed in the script. If I were targeting an attack on this, I could use curl to load the page, parse the token out, then send the malicious request using a good token. `curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Requested-With: XMLHttpRequest"));` defeats the other measure, and curl doesn't care about the Same Origin Policy. As I said, there isn't really a silver bullet here. – Chris Baker May 27 '16 at 16:32
-3

This isn't a problem that can be solved. If you create a website you make it, by definition, openly available.
If you want your data to be private, you need to require some sort of login.

It is impossible to create a system that is open to users but not to scripts without login/annoying captchas.

gnur
  • 4,671
  • 2
  • 20
  • 33