1

I have a client that makes an AJAX call to categorize a URL. It will call myserver.php?url=facebook.com and the server would respond Social. There are no passwords involved and just a URL string for which the server would return a category.

We have built a large database for url categories and I don't want people calling this API and stealing the data. What are the ways I can make sure that the request I'm getting at the server is my client? Would setting a request limits per IP work on the server side?

Is it worth going to SSL (as there are no ultra-secure stuff involved and I get 1000s of requests a minute)? I'm a data security novice, so kindly guide me on this.

Bryan Kaplan
  • 15
  • 1
  • 5

3 Answers3

2

Ultimately you cant secure any public resource from screen scrapping...Read More, but if your looking to just add a basic layer of protection from someone just scripting something that directly access's your sites API, then you can set a single use CSRF token on to the AJAX request, also a wise step is to not use GET and use POST instead.

Here's a quick example, upon the client loading the page you set some tokens into the session, and add the tokens to the AJAX:

<?php 
session_start(); 
$_SESSION['csrf_ajax_key'] = sha1(uniqid());
$_SESSION['csrf_ajax_val'] = sha1(uniqid());
?>
<!DOCTYPE html>
<html>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>

<body>

<span id="result">Ajax response goes here...</span>

<script>
var request = $.ajax({
    type: "POST",
    url: "yourAPI.php",
    data: {"url":"facebook.com", "<?php echo $_SESSION['csrf_ajax_key'];?>":"<?php echo $_SESSION['csrf_ajax_val'];?>"}
});
request.done(function(response) {
    $("#result").html(response);
});
request.fail(function(jqXHR, textStatus, errorThrown) {
    console.log(textStatus, errorThrown);
});
</script>

</body>
</html>

Then on your API do some simple checks to check that the keys are set, its a xmlhttprequest(AJAX) and is a POST request. Then unset the session keys to stop multiple requests or you could return new keys for subsequent requests (if your polling).

<?php 
session_start();

if(
    //Check required variables are set
    isset($_SESSION['csrf_ajax_key']) &&
    isset($_SESSION['csrf_ajax_val']) &&
    isset($_POST[$_SESSION['csrf_ajax_key']]) &&
    isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&

    //Check is AJAX
    strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest' &&

    //Check is POST
    $_SERVER['REQUEST_METHOD'] === 'POST' &&

    //Check POST'ed keys match the session keys
    $_SESSION['csrf_ajax_val'] == $_POST[$_SESSION['csrf_ajax_key']]
){
    //good - example
    if(isset($_POST['url']) && $_POST['url']=='facebook.com'){
        echo 'This is the response for facebook.com';
    }

}

//Unset to stop multiple attempts
unset($_SESSION['csrf_ajax_val'], $_SESSION['csrf_ajax_key']);
?>

Though its not 100% but will stop most.

Community
  • 1
  • 1
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
  • 1
    This depends on whether the page that the AJAX request is made from is public or not (maybe @Bryan could clarify). If so, an attacker could simply request the HTML page first, scrape the token and then make the AJAX request. – SilverlightFox Feb 21 '14 at 10:18
  • Yeppers, your right, I was going to add that but instead I added the statement at the bottom. There is no 100% way to protect a public resource. Ultimately if the browser can access it, the only option is security through obscurity. Though it could be said for any form that uses a CSRF type token, one could scrape it first, but does that mean that its not worth adding that extra layer... – Lawrence Cherone Feb 21 '14 at 14:13
0

Well you can look at some of the bigger players in this field. To access services from Google etc. you need an API Key, as a simple form of authentication. This can be transferred either as a Parameter or as a HTTP-Header.

SSL helps to prevent eavesdropping on the API Key.

And remember that using GET for this kind of informationen is not recommended, as the API key would otherwise be readable in proxy logs etc.

Chris
  • 612
  • 7
  • 16
0

Your should validate the URL passed in. You can do things like:

  • validate that the URL provided is a valid URL
  • disallow certain URLs like IP addresses (only if those aren't being categorized)
  • if you can, require authentication by the client with an account, which you can automatically ban if some number of invalid requests are made. In this case you need SSL.
  • limit the number of requests per second per client to prevent Denial of Service and unreasonable resource consumption
John Sampson
  • 544
  • 4
  • 11