Secure your Ajax requests: part 2
In the previous post, Bryan showed you how to prevent a JSON payload to be rendered across domains by simply adding a string of text at the beginning of the payload that either 1) invalidates it (script tags are only allowed to pull in valid JavaScript before they do anything) or 2) creates an infinite loop so the rest of your script never executes.
More issues
While this is a good way to effectively protect an attacker from pulling information they’re not supposed to access, what about pushing information or executing commands via POST / *GET? In this case it doesn’t really matter if the attacker can access the HTTP response or not – the attackers request has already executed and the response (success or failure) is relatively unimportant. Any potential damage has already been done.
For example, using the above methods an attacker may not be able to see a response from:
samplesite.com/user/ajax/getPrivateDetailsJSON.php
but using an image tag (or any other type of GET request), he may be able to make your browser fetch:
samplesite.com/user/ajax/setPrivateDetails.php?email=hack123@gmail.com
Redirecting your emails on the site to his disposable account. The unsuspecting user only sees a broken image on the page and is none the wiser. POSTs are only slightly safer as an attackers site (or one he’s successfully exploited) can POST using JavaScript to an iframe for the same result.
The worse part of this scenerio is that it’s not just limited to ajax; because ajax is just another way to do GET and POST reqests – any action on a site can be called from an outside source fully authenticated.
This issue is prevalent as even adding an image tag src’ing the MySpace logout action will log any MySpace users out that viewed the page: http://www.myspace.com/index.cfm?fuseaction=signout
Solutions
Use POSTs instead of GETs when modifying data
The first thing is to use POSTs instead of GETs whenever any action modifies data. POSTs can only be made with a form or JavaScript while a GET request can be made with an image tag, css declaration, js file – pretty much anything with a src or href. This is why Facebook retrieves any images you link via proxy and stores that file on their server. Keep in mind that this merely makes you less susceptible but doesn’t stop a good percentage of XSS attacks.
Use Authentication tokens
Second, is to use authentication tokens. Authentication tokens are merely strings that gets passed during transmission that have to match up with the original requests. A good example of this is in the Twitter form located at http://twitter.com/home. It contains the hidden input:
<input id=”authenticity_token” type=”hidden” value=”
304d4dfe3fgfd4ff68b3ad4ddf78adb5c09bc01d” name=”authenticity_token”/>
When the page is requested from the server, the form to post a twit has a hidden token. If you post the form without this value your request fails. It’s completely transparent to the user but makes spoofing this form outside of Twitter hard as the value is different per user and impossible to guess. Many people use a separate random hash for this, and some pass in the SESSIONID – both are good although the prior is slightly more secure. The only way to get a hack in on this type of action is to get an actual XSS vulnerability on the site in question that submits the form via JavaScript.
Use dynamic unpredictable URLs
Lastly, if your action is the same for every user then it’s easier to write an all-in-one hack for all users. If the action name or pathname has some variation to it depending on the user, it becomes that much harder to execute.
*Note that it’s never a good idea to modify data using a GET request.