If I have an AJAX call to a php script, like this (using jQuery)
$.ajax(url: "../myscript.php");
and myscript
looks like this:
<?php
//code that does something to db
?>
I want to know how to prevent a user from just going to example.com/myscript.php to execute the script.
Some answers here give you an overview of the concepts behind your question, let me give you a more pragmatic approach (you should at least read and understand what others say about this matter though!).
You just need to ask yourself: Do your app must enforce that all requests to myscript.php should be controlled?
If so then you need to use some sort of token: you create a token and send it to the client (browser), then the browser must send back the token and you check if it matches before doing some action:
<?php
// somefile.php (this file serves the page that contains your AJAX call)
session_start();
//...
$_SESSION['token'] = createNewToken(); // creates unique tokens
//add the token as a JS variable and send it back in your AJAX CALL
// some where you'll have something similar to this:
<script>
var token = <?php echo $_SESSION['token'] ?>;
$.ajax({
url: "myscript.php",
data: form_data, // include the token here!
//...
})
And then in your script:
<?php
// myscript.php
session_start();
// you can check if it's an AJAX call, if the user is logged and then the token:
if (!isset($_SESSION['token')) {
header("HTTP/1.0 403 Forbidden");
die("Direct access not allowed!");
}
// Assuming your AJAX is a POST though you didn't tell us
if (!isset($_POST['token'] || $_POST['token'] != $_SESSION['token']) {
header("HTTP/1.0 400 Bad request");
die("You didn't provide a valid token!");
}
// do something with your DB
Otherwise you just need to check if the user is logged as you would normally do with the rest of your scripts:
<?php
// myscript.php
session_start();
// Check if logged in user
if (!isset($_SESSION['loggedIn']) || !$_SESSION['loggedIn']) {
header("HTTP/1.0 403 Forbidden");
die("You need to be logged in!");
}
// Do something with your DB
TO SUM UP
Using the first method allows a more controlled access as you force the user to send a secret (the token, which will be different in every request) that a normal user won't have (if some other user gets the token, then you have bigger problems like session hijacking). Notice that this method prevents the user opening on multiple tabs / different browsers as only the last token will be saved. In order to avoid that you have this fantastic answer on SO
On the other hand, the second approach allows (logged) users to request directly your myscript.php, but maybe you don't need to prevent that (if you need, just use the first method). Notice here you won't have the issue of multiple tabs / different browsers as you'll only check if the user is logged in.
Ajax queries are just user queries
Every XmlHTTP request can be replayed and tampered (just check your favorite browser console, capture the POST or GET requests and check if there is a replay options), you can also try Live HTTP Headers module (or many more) and capture anything to replay it.
So if you set an entry point in your application, anybody can try to access it and inject some bad stuff there.
Note that they can also alter any HTTP headers in their requests to alter things like the referrer page or the host header, anything.
Insecure Inputs
So in term of security every user input has to be considered unsafe (GET parameters, POST data, used url -- OMG so much application are never filtering data coming from the url path --, cookies, ...)
Filtered output
So you may wonder "How can I do something with insecure inputs?", well ...you can. The rule is to filter all the outputs. Take the output canal (database storage, html page, json response, csv file) and escape your data accordingly (htmlentites for HTML, json escapes for json, sql escaper or parametized queries for SQL queries -- check the libs--), especially the parts coming from the user input, which are really unsafe as stated before.
Access control
Now your main problem here is access control, you have an entry point where you perform some database actions and you do not want anybody to access this entry point and perform actions.
Several things to do:
- ensure this is not a GET entry point (only POST, PUT, DELETE HTTP actions should perform modifications on the database), this will prevent usage of this url in an image tag later, loading the action without user interaction.
- manage a user session, using cookies (PHP does that for you) you can share some data between several HTTP requests, this is called a session. The user cookie will be used to load the server-side session storage, containing important data, such as Is my user an anonymous user or a connected one?. This is the Identification part.
- manage log-in log-out pages to get the Authentication part, theses pages will feed the session with the logged-in status. For a simple solution you can also check for HTTP basic authentication (.htpasswd files), it will also work for ajax, but never use HTTP basic Authentication without SSL. This Http auth mode will manage both identification and authentication parts.
- manage ACL (Access Control List), the way you want, and use that to decide if your ajax page can be accessed by the current user (you get the user from the session). If not, send a 403 HTTP response.
Public Access
Now if your 'database' stuff that should run is not related to any user privilege, but you just want to prevent abuse of it, like, say, a statistical ajax query, doing a counter increment, that every user should call at least once. In this case you will have some problems. It's very hard to prevent abuse of a public entry point (just think of how hard it is to protect websites from DOS and DDOS). You'll have to build a functional system, application-based, things like generating a unique token in the user page and checking that this token is used only once (but an anonymous page could be used by thousands of users, coming from a proxy cache), maybe you'll have to record user IP and restrict the token usage by IP (but some users may share the same IP), or maybe you'll have to send the unique token to the user using ajax.
We could talk of a lot of things, but that depends on the things you are trying to do. The important thing are:
- never trust user inputs
- filter outputs
- manage sessions and ACL
- never consider anything as hidden, there's no such thing.
how to prevent a user from just going to example.com/myscript.php to execute the script
From a security perspective, the AJAX call is the same as the user going to that URL. That is, the human user and the script you use to make the AJAX call are part of the same security principal. If you don't trust the user with access to the PHP script, you can't trust the JavaScript running on the user-controlled computer either.
So in what cases can there be separate security principals? You could, for example, only deploy the client JavaScript on some kind of tamper-proof kiosk. That way, you could store a secret value in the kiosk, shared with the server. The kiosk would send the secret value with each request for the server to validate.
But if you're doing this for a usability reason, to prevent accidental invocation of the script, then yeah, maybe try that one thing Dirk Pitt linked to.