From your requirements, it looks like an “average server code” relay (proxy) script - the best option.
PHP example here . However, for handling CURL errors, a new “object” is returned containing ['status'] ('OK' or CURL failure information) and ['msg'] containing the actual response from the API provider. In your JS, the original “now” API object requires one level down to “msg”.
Basic relays / proxies can be bypassed
If you use a relay script, then someone looking for an API key will probably try elsewhere. But; a pirate can simply replace his call to an API provider using your API key with a call to your script (and your API key will still be used).
Running your AJAX / relay script by search engines
Google bots (others?) Do AJAX. I guess (relaying or not) if your AJAX does not need user input, then bot visits will result in the use of an API key. Bots are "improving." In the future (now?) They can emulate user input, for example. if you select a city from the drop-down list in the API request, Google can change the parameters of the drop-down list.
If you are worried, you can enable validation of your relay script for example.
$bots = array('bot','slurp','crawl','spider','curl','facebook','fetch','mediapartners','scan','google'); // add your own foreach ($bots as $bot) : if (strpos( strtolower($_SERVER['HTTP_USER_AGENT']), $bot) !== FALSE): // its a BOT // exit error msg or default content for search indexing (in a format expected by your JS) exit (json_encode(array('status'=>"bot"))); endif; endforeach;
Relay script and additional code to solve the above problems
Do not overdo it with pirate protection; relays should be fast and invisible to visitors. Possible solutions (without experts and rusty sessions):
1: Solution for PHP Sessions
Checks to see if someone who visited your AJAX page in the last 15 minutes has called, provided a valid token and has the same User Agent and IP address.
Ajax pages add the following snippets to your PHP and JS:
ini_set('session.cookie_httponly', 1 ); session_start(); // if expired or a "new" visitor if (empty($_SESSION['expire']) || $_SESSION['expire'] < time()) $_SESSION['token'] = md5('xyz' . uniqid(microtime())); // create token (fast/sufficient) $_SESSION['expire'] = time() + 900; // make session valid for next 15 mins $_SESSION['visitid'] = $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']; ... // remove API key from your AJAX and add token value to JS eg $.ajax({type:"POST", url:"/path/relay.php",data: yourQueryParams + "&token=<?php echo $_SESSION['token']; ?>", success: function(data){doResult(data);} });
Relay / proxy script (session version):
Use the existing sample relay script before adding the CURL block:
session_start(); // CHECK REQUEST IS FROM YOU AJAX PAGE if (empty($_SESSION['token']) || $_SESSION['token'] != $_POST['token'] || $_SESSION['expire'] < time() || $_SESSION['visitid'] != $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) { session_destroy(); // (invalid) clear session variables, you could also kill session/cookie exit (json_encode(array('status'=>'blocked'))); // exit an object that can be understood by your JS }
Assumes default session settings. Required cookies and page / relay in one domain (possibly working). Sessions can affect performance. If the site already uses sessions, the code should consider this.
2: Carefree / No Cookie
Uses a token associated with a specific IP address and a User Agent valid for a maximum of 2 hours.
Functions used by both the page and the relay , for example. "Site-functions.inc":
<?php function getToken($thisHour = TRUE) { // provides token to insert on page or to compare with the one from page if ($thisHour) $theHour = date("jH"); else $theHour = date("jH", time() -3600); // token for current or previous hour return hash('sha256', 'salt' . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . $theHour); } function isValidToken($token) { // is token valid for current or previous hour return (getToken() == $token || getToken(FALSE) == $token); } ?>
Relay script Use an existing example before adding a CURL block:
// assign post variable 'token' to $token include '/pathTo/' . 'site-functions.inc'; $result = array('status'=>'timed out (try reloading) or invalid request'); if ( ! isValidToken($token)) exit(json_encode(array('msg'=>'invalid/timeout'))); // in format for handling by your JS
Pages requiring an API (or your javascript-enabled file):
<?php include '/pathTo/' . 'site-functions.inc'; ?> ... // example Javascript with PHP insertion of token value var dataString = existingDataString + "&token=" + "<?php echo getToken(); ?>" jQuery.ajax({type:"POST", url:"/whatever/myrelay.php",data: dataString, success: function(data){myOutput(data);} });
Note. User agent is faked. IP (REMOTE_ADDR) cannot be faked, but setting up on fewer sites can cause problems, for example. if you are behind NGINX, you may find that REMOTE_ADDR always contains the NGINX IP address.
If you use a typical third-party API that will provide confidential NON information until you reach the usage limit for your API key, then (I think) the above solutions should be enough.