I have a PHP page where only other sites can access for polling (sort of like a webhook). I don't want any users to try to access/visit it. How can I make sure it is accessed by a specific source/way?
For example:
When the website checks for new data on my site, they will visit a page called check.php. They will be sending POST and GET info to check. Here is an example code I have for that page:
<?php
if(empty($_GET['name']) || empty($_GET['email']) || $_POST['secret-code'] !== 'abc123') {
echo "error";
exit();
} else {
$name = $_GET['name'];
$email = $_GET['email'];
echo 'Here is the info you requested...';
}
?>
How can I secure this even more? I want to make sure no one can access this if they were to send their own $_GET and $_POST parameters to request data. Anything I can do with code or even headers? Thank you for any help
One option would be to verify the specific user data from the request, such as a predetermined password or key. This is the least secure method.
Another option would be to only allow network connections from a single source. You could do this by checking the IP of the client each time they make a request. If you take this route make sure to verify by password as well since IP spoofing is possible.
An even more secure way would be to take a part of the client information request and send it right back to the client, verifying that they sent you this information (this would be like 2-factor authentication).
The most secure method would be having the client create a cryptographically signed key, which you could then verify only could have been signed by them.
This could be done using the openssl_sign() and openssl_verify() functions.
Why not just use a token that you can store on your database and read it from the headers just like an API?.
You can just check if the header exists and have the token value and check against your database that the token is valid and you can do some other things like logging the requests and stuff like that and add new tokens easily.
If the header doesn't exists and the token is not valid, then, remove the access...
Or, you can do an "ip whitelist" so only the requests from certain IPs can pass or domains like a "CORS" but limited to certain IPs and domains.
Because in that way is easier to manage the access to certain users on your service. Another options (including the safer) is like #shn said.
Related
I want to use post to update a database and don't want people doing it manually, i.e., it should only be possible through AJAX in a client. Is there some well known cryptographic trick to use in this scenario?
Say I'm issuing a GET request to insert a new user into my database at site.com/adduser/<userid>. Someone could overpopulate my database by issuing fake requests.
There is no way to avoid forged requests in this case, as the client browser already has everything necessary to make the request; it is only a matter of some debugging for a malicious user to figure out how to make arbitrary requests to your backend, and probably even using your own code to make it easier. You don't need "cryptographic tricks", you need only obfuscation, and that will only make forging a bit inconvenient, but still not impossible.
It can be achieved.
Whenever you render a page which is supposed to make such request. Generate a random token and store it in session (for authenticated user) or database (in case this request is publicly allowed).
and instead of calling site.com/adduser/<userid> call site.com/adduser/<userid>/<token>
whenever you receive such request if the token is valid or not (from session or database)
In case token is correct, process the request and remove used token from session / db
In case token is incorrect, reject the request.
I don't really need to restrict access to the server (although that would be great), I'm looking for a cryptographic trick that would allow the server to know when things are coming from the app and not forged by the user using a sniffed token.
You cannot do this. It's almost one of the fundamental problems with client/server applications. Here's why it doesn't work: Say you had a way for your client app to authenticate itself to the server - whether it's a secret password or some other method. The information that the app needs is necessarily accessible to the app (the password is hidden in there somewhere, or whatever). But because it runs on the user's computer, that means they also have access to this information: All they need is to look at the source, or the binary, or the network traffic between your app and the server, and eventually they will figure out the mechanism by which your app authenticates, and replicate it. Maybe they'll even copy it. Maybe they'll write a clever hack to make your app do the heavy lifting (You can always just send fake user input to the app). But no matter how, they've got all the information required, and there is no way to stop them from having it that wouldn't also stop your app from having it.
Prevent Direct Access To File Called By ajax Function seems to address the question.
You can (among other solutions, I'm sure)...
use session management (log in to create a session);
send a unique key to the client which needs to be returned before it expires (can't
be re-used, and can't be stored for use later on);
and/or set headers as in the linked answer.
But anything can be spoofed if people try hard enough. The only completely secure system is one which no-one can access at all.
This is the same problem as CSRF - and the solution is the same: use a token in the AJAX request which you've perviously stored eslewhere (or can regenerate, e.g. by encrypting the parameters using the sessin id as a key). Chriss Shiflett has some sensible notes on this, and there's an OWASP project for detecting CSRF with PHP
This is some authorization issue: only authorized requests should result in the creation of a new user. So when receiving such a request, your sever needs to check whether it’s from a client that is authorized to create new users.
Now the main issue is how to decide what request is authorized. In most cases, this is done via user roles and/or some ticketing system. With user roles, you’ll have additional problems to solve like user identification and user authentication. But if that is already solved, you can easily map the users onto roles like Alice is an admin and Bob is a regular user and only admins are authorized to create new users.
It works like any other web page: login authentication, check the referrer.
The solution is adding the bold line to ajax requests. Also you should look to basic authentication, this will not be the only protector. You can catch the incomes with these code from your ajax page
Ajax Call
function callit()
{
if(window.XMLHttpRequest){xmlhttp=new XMLHttpRequest();}else{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}
xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){document.getElementById('alp').innerHTML=xmlhttp.responseText;}}
xmlhttp.open("get", "call.asp", true);
**xmlhttp.setRequestHeader("X-Requested-With","XMLHttpRequest");**
xmlhttp.send();
}
PHP/ASP Requested Page Answer
ASP
If Request.ServerVariables("HTTP_X-Requested-With") = "XMLHttpRequest" Then
'Do stuff
Else
'Kill it
End If
PHP
if( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && ( $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' ) )
{
//Do stuff
} else {
//Kill it
}
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.
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.
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.
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.
I am creating an API for my website which has lots of information, for say, movies. I want to allow certain number of requests. So, for example, 5$ plan allows 10,000 requests a month. User sign ups, gets the API key and then can make a request like
http://website.com/index.php?api_key=API_KEY&movie=Titanic
and the server gives back the answer in json. My question now is, how can I make sure that this API_KEY can be used just by that user? Because if he makes an AJAX request, someone else can see the link with the API_KEY and use it for his project. And I want to allow AJAX requests.
Whoever holds that API_KEY should be considered "that user", so if you want to keep "atomic" requests (where api_key is part of each request, and no request need to depend on previous ones) then you can't really do much about it. But you can try then other approach where you change the way your API works, by getting rid of said "atomicness". In that model it would require any api method to call with session_key instead of the api_key, and your api_key should only be used to generate temporary session_key (kind of login but for API - say login method there). Then all further calls should require session_key login returned. In that case you can control (and limit) the number of sessions created with the single api_key, or i.e. terminate other sessions if new login is called.
Why don't you try sending your API_KEY in custom headers which can be triggered along with php or ajax requests from the client side, that way your API_KEY is not visible in the URL at all, later which can be ripped of from the server using apache_request_headers(), it is a much safer approach, if you are still not satisified you can implement HMAC http authentication, which is damn safe, I bet you.
Approach 1:(without HMAC)
Client Side:
using Jquery
$.ajax({
url: 'foo/bar',
headers: { 'api_key': 'API_KEY' }
});
Using PHP
header('api_key':'API_KEY');
by adding header with the request the URL can be just
http://website.com/index.php?movie=Titanic
On serverside:
$headers = apache_request_headers();
if(isset($headers['api_key']))
{
// validate your api_key from database
}
Approach 2: (with HMAC)
In this case, there is a slight change, instead of sending API_KEY in your url as query string , you will have to send userID or any other unique identifier, inorder which is related to the api_key in your database.
on client side:
do HMAC as given in https://code.google.com/p/crypto-js/#HMAC
or using php function hash_hmac()
using both methods you will get a hashed value which is a combination of your API_KEY , and your data
eg.
$hashed_value = hash_hmac('sha1','titanic','API_KEY');
header('hash':$hashed_value);
your url http://website.com/index.php?movie=Titanic&uid=xx
On server side:
$headers = apache_request_headers();
if(isset($headers['hash']))
{
// then try to recreate your hash in server, like
//using $_GET['uid']= get your API_KEY from DB
$api_key = getApiKey($_GET['uid']);
$hash = hash_hmac('sha1',$_GET['movie'],$api_key);
if($hash==$headers['hash'])
{
// User authenticated
}
}
If you go through HMAC, you can figure out its benefits.
If the API key is made visible on the client in any way, shape or form, users who visit that page can also make requests on the API user's behalf, as long as they are "power user" enough to inspect the code and/or HTTP requets. Regardless of whether you have encryption, whether you accept the key in the URL, cookie or whatever... Those other methods may be a slight inconvinience, but a user that knows enough to start searching for the API key is likely also knowedable enough to have heard about Fiddler and the like.
This is the very reason why APIs typically don't make themselves available to JavaScript (i.e. AJAX), but only to direct requests - the server will be the one making the request, and thus it wouldn't need to reveal their API key to its users that way.
There's simply no solution to this, short of not allowing AJAX to use the API. The only kind of an API you should allow AJAX access to is a publically available API (as in "no login required"), limited only by IP making N amount of requests in N amount of time instead.
It's not possible using just (effectively public) API keys. What you need is cryptographic authentication with secret keys. TLS with client side certificates ought to do it.
Perhaps not a 100% perfect solution but for your particular movie business example it should work out just fine.
First you give each user a username and password.
In order to make an AJAX call user will have to specify 3 things:
username
desired movie id
API_KEY
Now, the API_KEY will be a hash (you can use sha1() or md5() or even a combination of them) that is generated based on:
user's password
desired movie id
today's date
So here's approximately what user will have to do in order to make an AJAX request:
$username = 'john_smith';
$password = 'abc';
$movie_id = 'Titanic';
$date = date('Y-m-d');
$API_KEY = sha1($password . $movie_id . $date);
AJAX("http://website.com/index.php?user=$user_name&movie=$movie_id&api_key=$API_KEY");
(Similar can be done in JavaScript in case you are expecting users to only have client side environment.)
On your side you will have to do as follows. Knowing the username you find out their password in the database. Get the movie_id from the request. And today's date we all know. (You can also check for yesterday and tomorrow to avoid time-zone issues.)
Then you generate the same hash and compare it to the API_KEY that user sent you. If they match - all good.
This way you will end up with unique AJAX calls that are valid for one movie only and will expire in about a day.
I think that would be a pretty efficient approach, at least business-wise.
instead of having all the data in the url, why not use the POST method, and have all the data sent behind the scenes. you could then post their id and other data between pages (using hidden fields) or using a session.
You can pull cookies in a web service. The web server then can use the authentication cookie to authenticate the web service call. I'm sure what ever frame work your using has an authentication token stored in a cookie on the browser somewhere and there's a snipet of php somewhere to authenticate it.
Danger not a php example here.
I have an authenticated web service on my web site http://www.gosylvester.com that just returns a hello world but if you put a trace on it and sign in. You'll see the authentication cookie go up and down with it.
Your welcome to play around with the web service. There's a button on the main page that says "ASP authenticated Test Button It says hello unless you are signed in" put a trace on your browser and click it.
user guest password abc123$
You can also log in via webservice on my site but that's another post
Good Luck
If you really want it secure, use Oauth authentication (2-legged variant, for your case). Of course, you could also use HMAC authentication, or key exchange, as other authors suggested, but it's always a better idea to use existing and verified protocols, not invent your own.
See:
Securing REST APIs
Using Oauth to protect internal REST API
I am making an AJAX request to a script called return.php that will look like this:
http://www.example.com/return.php?type=3&filter=2
This occurs when a browsing user hits a button on example.com
I want additional security measures so that this can only be requested by a user browsing my site. No direct type ins, etc.
So I was thinking to send some type of randomly generated key along with the request. What methodology would I use to verify at return.php that a correct key has been sent?
Can I generate a key and store it in a session variable that is then accessible in return.php?
pseudo code:
if ($random_key_sent == what it should){
//go ahead and execute code
}
else{
//sorry can't help
}
And Ultimately my request would look something like:
http://www.example.com/return.php?type=3&filter=2&key=8fu5jtugy489kgvuri09984ufjlgi (or whatever the key would be)
Bottom line I am looking for a way to generate some type of added security so that return.php is only being used when it should be, along the similar lines of using API keys etc.
You're looking for cross-site request forgery (CSRF or XSRF) protection.
Typically, you generate and save an anti-CSRF token in the user's session data, and put it in a hidden form field (using either GET or POST), or for normal links you place the token in a query parameter. On the server side, you check that the anti-CSRF token matches the one in the user's session data.
Some suggest that you can achieve the same level of security by simply checking the HTTP referer header. That can work, but is less reliable since people can block the referer for privacy reasons.
If you are worried about "direct type-ins" but still need to use GET requests, you can check the request headers in PHP to only allow Ajax requests using $_SERVER['HTTP_X_REQUESTED_WITH'].
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
// Do what you need to do
} else {
die("Ajax Only");
}
(Modified from David Walsh)
Unless you're going to have a list of preset keys that your PHP code checks against and the browser can receive to then send in the URL, it would be impractical to check the key for a specific value. You could just check if the $_GET["key"] array value is set:
if(isset($_GET["key"])){
// go ahead and execute code
}else{
// sorry can't help
}
Alternatively, you may want to consider requiring the browser to set some POST data which is sent along with the request to the webpage, but cannot be typed into the address bar. Then just use $_POST["key"} rather than $_GET["key"] to verify.
I have a PHP script hosted on my site that outputs a value based on the GET parameters passed.
Other sites call this script from within their own PHP scripts via the PHP function file_get_contents with the url and get params and are served back just the value requested.
I am trying to allow only certain domains access to this script and have been using HTTP_REFERER to check who's calling the script.
if (isset($_SERVER['HTTP_REFERER'])) // check if referrer is set
{
echo $_SERVER['HTTP_REFERER']; // echo referrer
}
else
{
echo 'No referrer set'; // echo failure message
}
I am getting No referrer set when I use file_get_contents but if I use a clicked link from a page to a script with the above code the referrer displays correctly.
Am I using the wrong function (file_get_contents) to call the external script and can someone suggest the correct one or should this work?
Any help much appreciated.
Thanks
Bear in mind that the HTTP "Referer" header is an optional header -- there's no need for a site to send it to you, and it can be easily faked. If you really only want certain people to use your resources, you're better off using some form of authentication.
Typically Referer: is sent by web browsers, but there's no need for it to be -- for example, they won't send it if the referer is a secure site. With a PHP file_get_contents() there isn't technically a referer anyway; you're not being "referred" from anywhere.
Consider instead either:
Locking down by IP address (but bear in mind that multiple domains can share a single IP, and that a domain's IP can change.)
Using some form of authentication (preferably not one that transmits passwords in plain text!)
You should consider how secure you need this service to be, and what threats might attack it when deciding the right security to apply.
You would be much better to restrict based on IP address rather than domain, much more reliable. Just keep an array of allowed IP's and call in_array($_SERVER['REMOTE_ADDR'],$allowedAddresses) to validate it.
Or just require authentication via a cookie or HTTP auth...
You can't do this using HTTP_REFERER.
The HTTP_REFERER it set by the client, and it can be anything the client wants.
You have to use a password / key authentication mechanism instead.
May want to use something along the lines of a stream context to set extra headers.
http://us.php.net/manual/en/function.stream-context-create.php
Additionally, if needed, you could set a 'secret' header to authenticate the requests, rather then the referer.