I think you can (at least partially) solve your problem with cryptography.
Say you have a page main.php which includes JS to call another page called ajax.php. When the page main.php is accessed, use a $browserKey and a $salt to create a $_SESSION["tempHash"]. (You also need to store the salt.) Then, give the key to the JavaScript making the request to your page ajax.php, and check that the key and salt give the same hash as before. On main.php, do something like this:
<?php
session_start();
// authorize user here
$salt = time();
$browserKey = mt_rand();
$hash = sha1("$browserKey$salt");
$_SESSION["tempSalt"] = $salt;
$_SESSION["tempHash"] = $hash;
// ... code ...
?>
<!doctypehtml>
<html>
<!-- html -->
<script>
// ... ajax call ...
var params = "param1=val1¶m2=val2&...&browserKey=<?= $browserKey ?>";
request.send(params);
</script>
</html>
<?php session_write_close(); ?>
Then, on ajax.php, simply do
<?php
$validated = false;
if(sha1($_POST["browserKey"] . $_SESSION["tempSalt"]) === $_SESSION["tempHash"]) {
$validated = true;
}
// and unset the salt and hash
$_SESSION["tempSalt"] = $_SESSION["tempHash"] = null;
if(!$validated) {
// taken from another SO answer:
// http://stackoverflow.com/a/437294/2407870
header('HTTP/1.0 404 Not Found');
echo "<h1>404 Not Found</h1>";
echo "The page that you have requested could not be found.";
exit();
}
// else, continue normal processing here
?>
I'm not an expert in this domain, so take my advice with a grain of salt. (Heh, cryptography joke.)
One potential vulnerability with this approach is that the person may load page main.php and then wait five hours and call page ajax.php. It will still only allow them to access it once though. And you can do other things to prevent this. E.g, check the salt (which was obtained with time()) to verify that not too much time has passed. Or even send periodic heartbeats to the server, which generate a new hash, salt and key, returning the new key to the browser.