PHP JavaScript Security – How to Validate AJAX Request Source

ajaxhtmljavascriptPHPSecurity

I am building a simple Q&A app with PHP, HTML and JS. I have three tables: users,question and answers, each table has it's own primary key and questions and answers both have foreign key constraints. questions store users.uID to look up author, and answers store question.qIDto identify which question they belong to, and also author that wrote the answer, so users.uID is also foreign key.

And here is my main concern: how to properly implement submit button for answer, that passes the author ID and also question ID that is supposed to be inserted in answers table via ajax request, so it has to get that ID from somewhere. The main concern is not XSRF, but the fact that authorised user could tamper with it, by changing a part of html code if the ID is stored visibly (or invisibly) somewhere.

User ID is easy, that can be stored in session once retrieved from DB and user logs in and will be reused elsewhere too so no big deal in term of performance costs, and can be accessed by all scripts while hidden from user. But also storing the question ID in session, each time user visits a question, and then replacing that with another question ID when he visits another question seems to me like it is asking for causing heavy load on server at some point.

Adding hash to the submit button, that is then validated by script that processes the ajax call data looks cumbersome to me too, since you have to store that hash in session too. And using hidden field is not safe either, since user can see that field in source code and can tamper with it.

So where to efficiently store that question ID, so it is ready when the submit button is pressed and also that user cannot tamper with it, and send ajax call with other ID? Is the session really only option?

Best Answer

The main concern is not XSRF, but the fact that authorised user could tamper with it, by changing a part of html code if the ID is stored visibly (or invisibly) somewhere.

I understand the desire to make sure that the user does not circumvent the user interface you provided. However, this is not easy to do, and will not result in any improved security.

  • You would have to store the relevant user state server-side and compare it with the state provided by the user. Since the same state is now in two places, there's always the possibility that these states fall out of sync, e.g. if the user aborts an action or if the network connection is interrupted. The user may also have multiple states, e.g. if they are using your web app in multiple browser tabs.
  • You could also create a cryptographic signature of the user state, and give the user this signature as a token. The user must transmit the token with each request, so that it can be validated by the server. However, there's still the possibility of replay attacks if this token isn't implemented as a nonce.

All of that is easy to get wrong, and may cause a lot of frustration for your users if it doesn't work as intended (I've encountered a couple of sites that are sometimes unusable unless I delete some cookies). This effort is certainly justified for online banking or similar problem domains where you'd rather be safe than sorry. In other domains, this is more likely to be perceived as user-hostile.

Instead of checking that the user sticks to the links you provided, check that the user has the necessary permissions for any action they are trying to take.

  • If they are generally allowed to view, create, or modify a resource, allow the user to take that action even if they manipulated the URL.
  • If they are not allowed to access the resource, deny them even if they can guess the correct URL. A number of data “breaches” had the cause that private data was not protected by a permission model, but only by a “secret” ID that other users weren't supposed to know – but could be guessed or intercepted by the attacker.

To defend against abuse of the resources that user is allowed to access:

  • Use rate limits. E.g. in a Q&A system, a normal user can't and shouldn't post 5 answers within 10 minutes.

  • Develop a system for flagging and review of user-generated content, especially if user content is publicly visible.

  • If users are resorting to bots when using your web app, this could mean that you are lacking some crucial functionality. Offer an API that is more convenient than scraping. Sometimes, an export-to-CSV button is all your users are missing.