I've mentioned Cross-Site Request Forgery (XSRF) here before; It's a lesser-known cousin of the 'sexier' web vulnerabilities such as Cross-Site Scripting (XSS) and SQL Injection. To over-simplify it to a near extreme, XSRF allows a non-authenticated user of a web site to take certain actions that authenticated users can do. For example, if a user has the capability to choose who to send 'Friend' requests to on a site, such as Facebook, as well as removing previously-added friends. If that functionality is not coded correctly, a malicious user could craft a special page that would force the victim user to unknowingly perform such actions.
The actions I mention may seem not so horrible. However, consider that your Facebook profile may have areas marked as private only to friends. Also, though, such vulnerabilities are not limited to doing that sort of thing: I've seen demonstrations that purport to have enabled online banking funds transfers using XSRF. XSRF can commonly be used to delete and deface content on web sites, elevate the privileges of a malicious user on the web site, and do many other things.
The Solution: The Short Version, Again
The quickest and easiest solution to XSRF vulnerabilities is properly using the HTTP protocol. GET requests should never cause additions, edits or deletions to the data of a web site. Put simply, this is what permits XSRF attacks. GET requests should only display information; 'Actions' which change something should be done with a POST request (or 'PUT' or 'DELETE', if available to you).
So recently, I reviewed a site for a client* which had code similar to this throughout the site:
<form action="/destination.url" method="post">
<input type="hidden" name="some_id" value="[some_id_value]" />
<input type="submit" value="Do The Thing" name="submitted" id="submitted" />
</form>
This form looks good, right? It uses method="post", so it's safe. Right?
Not So Fast
The forms themselves are, in fact, all correct for protecting against XSRF. Further, there are no Anchor tags generating simple links that perform 'actions' via GET requests, either. Nevertheless, every single such form was, in fact, vulnerable to XSRF, across the whole site. How can that be?
The Action Pages Used Generic Request Collections
The pages that each form action pointed to were using generic request collections, and were not otherwise checking that the request was a POST request. So, those forms were submitting POST requests, but the page accepting the form data did not care; they sought each parameter name among the Form, QueryString and Cookies collections via the programming framework's generic mechanism. For instance, if this were ASP/ASP.NET:
var some_id = Request.Form["some_id"];
var some_id = Request.QueryString["some_id"];
var some_id = Request.Cookies["some_id"];
var some_id = Request["some_id"]; // Generic Version
The first looks among the Form values, which will only be sent in a POST request. The second looks among the QueryString values, which are the values you can see in the address bar and can be sent via any type of request (GET, POST, etc). The third looks for a cookie. The fourth, though, looks for the value within any of the above three collections (not strictly true for the Cookies collection in ASP.NET, actually) and returns the string value if it finds it. PHP has the same constructs; These do the same things as the ASP/ASP.NET equivalents respectively:
$some_id = $_POST['some_id'];
$some_id = $_GET['some_id'];
$some_id = $_COOKIES['some_id'];
$some_id = $_REQUEST['some_id']; // Generic Version
The site in question was using the Request[] or $_REQUEST[] collections instead of what they should have been using, Request.Form[] or $_POST[]. So, for every form, one could simply built a submission to the action page using query string parameters instead, and they would cause the action in question to run dutifully. Slipping these URLs into some img and/or iframe tags and causing people to navigate the pages with those links proved the concept; causing those users to unknowingly 'do' whatever the constructed link intended. (add friends; transfer money; delete content; send spam messages; etc)
This turned out to be a fairly easy fix, relatively speaking; but only because of the way they had organized their code so we could easily identify the submitted data which should have been sent via GET. Once the action pages only checked the POST/Form values, the maliciously constructed URLs no longer did anything at all - since the action page could no longer find the parameters.
* Note: I have permission from that client to discuss this issue in a generic way, which does not identify them at all. The issues noted herein have been fixed.
- For the curious; I provide security reviews of web applications for this and numerous other issues. The cost can be very low for a smaller site, and I am very up-front about any charges that might be incurred before I do any billable work.
- I am also still seeking a like-minded, freelance web developer who would like to trade out doing a modest amount of code/application reviews for each other from time-to-time.
- Use the 'Contact' page to inquire further.