7

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.

Peteris
  • 3,548
  • 4
  • 28
  • 44
  • clarify your question please .... – Sameh Serag May 17 '12 at 15:25
  • 7
    [You should not use GET for any purpose other than data retrieval.](http://tools.ietf.org/html/rfc2616#section-9.1.1) – Gumbo May 21 '12 at 08:32
  • It's just easier to use GET an example, I would probably be using POST anyways. – Peteris May 26 '12 at 09:38
  • 2
    Sadly, cryptography is not a magic wand that you can wave over anything to make it only do what you want. Some people spend billions of dollars to figure that out (the entire DRM industry is trying very, very hard to square that particular circle). Don't be those people. – Enno May 26 '12 at 18:27
  • possible duplicate of [Prevent Direct Access To File Called By ajax Function](http://stackoverflow.com/questions/1756591/prevent-direct-access-to-file-called-by-ajax-function) – IMSoP Apr 23 '13 at 20:40

8 Answers8

7

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.

lanzz
  • 42,060
  • 10
  • 89
  • 98
  • I was afraid this would be the case. If anyone has any obfuscation tricks to suggest, I'm all ears. – Peteris May 26 '12 at 20:41
4

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.

Rahul Prasad
  • 8,074
  • 8
  • 43
  • 49
3

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.

Enno
  • 1,736
  • 17
  • 32
  • 1
    This is the best explanation I've seen on the various duplicate questions. You should add it to http://stackoverflow.com/questions/1756591/prevent-direct-access-to-file-called-by-ajax-function which is the oldest dupe of this question. – IMSoP Apr 23 '13 at 20:46
  • You're right, @IMSoP, there is a lot of incompetence in that thread's answers. I adapted my reply a little bit, because what the OP really needed was to be pointed in the direction of authentication. – Enno May 12 '13 at 16:27
1

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.

Community
  • 1
  • 1
Andrew Leach
  • 12,945
  • 1
  • 40
  • 47
1

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

symcbean
  • 47,736
  • 6
  • 59
  • 94
  • 1
    This is not quite the same problem as CSRF. In CSRF, it is an identity issue, but I'm worried that the user himself might abuse their own login credentials, their own session token, and use their own account to artificially populate the database. – Peteris May 21 '12 at 11:14
  • Given that it is impossible to restrict access to the server to just your ajax APIs, it's exactly the same as CSRF – symcbean May 22 '12 at 08:34
  • 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. – Peteris May 22 '12 at 21:16
  • Why don't you use a token that's valid only for a short lifespan? (But enough to complete your AJAX request). You can generate a code based on server's time with a secret algorithm, implemented in both client and server. The client will add this code to its request, and the server will check if the code is still valid. Something like IPsec's "trick" to avoid replication attacks. If the number is not valid, the request will be just trashed. Please note that this is just an idea, I've got no working code for it! – StepTNT May 26 '12 at 19:06
0

It works like any other web page: login authentication, check the referrer.

Diodeus - James MacFarlane
  • 112,730
  • 33
  • 157
  • 176
  • Could you clarify? Are you saying that the server application will know who is issuing the request and that it's possible for it to recognize AJAX requests only? – Peteris May 17 '12 at 19:02
  • See: http://stackoverflow.com/questions/7127686/blocking-non-ajax-requests-to-php Session authentication is up to you. You can give the client a key that is passed back with each request. – Diodeus - James MacFarlane May 17 '12 at 19:05
  • I can't really find any safe solutions there. I've considered sending client authentication, but then the client himself can sniff around the headers and reuse the key for their own sake. There must be a more robust solution, because all web apps need this. – Peteris May 17 '12 at 19:54
  • 1
    Relying on the referer is not a good idea. OTOH, there is NOTHING you can do using AJAX that a user cannot replicate via other means. – symcbean May 21 '12 at 08:27
  • It's fairly easy to fake referrers. – Enno May 25 '12 at 00:29
0

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
}
Bora Alp Arat
  • 2,185
  • 3
  • 16
  • 21
  • 1
    Thanks for the effort, but that's just a header check as others have mentioned. It's not secure enough, since the user can fake the header. – Peteris May 24 '12 at 15:46
0

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.

Gumbo
  • 643,351
  • 109
  • 780
  • 844