Daniel is a business and technical systems analyst with a background in IT security and software development. He has six years experience in the IT security field, including published academic research. His main areas of expertise include software assurance, network security, and authentication. In addition to security, Daniel has a software development background in languages such as Java, Perl, SQL, and PHP. He also has 14 years experience working with and administering various versions of Linux and related open-source software.
Secure Development - Cross-Site Request Forgery (CSRF)
Feb 26 2010
Last week, we talked about Cross-Site Scripting (XSS) and briefly touched on Cross-Site Request Forgery (CSRF). These two attacks are very common and dangerous, which explains why they consistently rank among the top five web application vulnerabilities in almost all recent studies. This week, we'll go into more detail on CSRF. First, a quick reminder about the difference between these two attacks: XSS involves injecting unauthorized code into web pages, while CSRF involves making unauthorized requests that appear to come from a legitimate, logged-in user. Another way to think about the difference is that XSS abuses the user's trust in the web application, while CSRF abuses the trust of the web application in the user.
CSRF is essentially an in-browser session hijacking: the attacker first places scripting code into a trusted context inside the victim's browser (it's often injected via XSS). At this point, the code is free to take actions on behalf of the user who is currently logged on, and these actions are often completely indistinguishable from the actual clicks and text inputs of the user.
For example, if an attacker wanted to steal money from the victim's online banking account, he could inject code that would visit the following (fictitious) URL from the context of the victim's web session:
https://bank.com/transferFunds.do?amount=9500&destAcct=0928672954
Since the user is already logged into the site, the attacker's malicious request simply sends the existing session cookies along with the request, and it will be processed without any further authentication in the majority of cases. In fact, the receiving server will not be able to tell the difference at all between this request and a legitimate user-generated click.
There are a number of ways to inject CSRF code into a trusted context, but the net result is the same once the attacker succeeds in getting his code to run in the context of the victim's session.
- XSS: The code could be injected via an XSS attack, which would give it full access to all site-specific information (including session cookies) under the same-origin policy (see last week's XSS post for details).
- Browser Plugins: Malicious browser plugins (called browser helper objects, or BHO's, in the Internet Explorer world) can re-write HTML output inside the browser.
- Network Compromise: Another possibility is a man-in-the-middle attack on a user's entire network connection, which is not too far-fetched in the case of an unencrypted public wireless access point and an insecure HTTP connection (as opposed to HTTPS).
Out of these three, an XSS attack is the easiest to implement and the most versatile, since a stored XSS attack can be performed once on a server and can affect thousands of users without any prior compromise or even knowledge of the victims. Such an attack is also completely platform-independent (watch out, Mac and Linux users!), since the compromise occurs server-side and the injected JavaScript is executed as trusted by all browsers, regardless of the underlying OS. The other attack vectors first require more elaborate, targeted attacks as a springboard.
There is even a "low-tech" way to perform a CSRF attack that doesn't involve any coding or code injection: an attacker could purchase a banner ad on the target site, then embed the desired URL in an <img> tag. Any user who visits the site and views the ad will also inadvertently visit the URL desired by the attacker, again in the context of the user's current session. Finally, keep in mind that CSRF attacks are by no means confined to JavaScript: they can be executed via other browser-based technologies such as Flash, which provide a powerful programming interface.
CSRF attacks are difficult to detect, and even harder for users to explain away because they appear identical to the user's own actions. However, there are three main ways web application developers can mitigate the risks posed by CSRF:
- Use GET and POST Appropriately: In general, GET requests should not be used to send user actions to the server (as in the banking example above). The guideline is that GET requests should be used for static pages and queries (data flows from the application to the user) and POST requests for all user updates (data flows from the user to the application). Keep in mind that this is not a hugely effective countermeasure as it doesn't prevent injected JavaScript code from using something like an XMLHTTPRequest to submit POST requests behind the scenes. However, it does defeat the simple <img> embedding attack describe above since this would only work for GET URLs.
- Prevent Replays via One-Time Tokens: For all user updates, a web application should generate a one-time token that only allows the update to occur once. An attacker will not be able to visit a simple URL or even replay a previously observed URL to trigger the action on behalf of the user. Again, this approach is not foolproof: injected CSRF code could potentially request a valid one-time token from the application (just like the user can) and use it behind the scenes, without any user interaction. However, it is currently the most common approach to CSRF prevention, and the OWASP project has created an open-source framework for J2EE called CSRFGuard.
- Challenge-Response: This approach is reserved only for the most critical actions a user takes, such as changing his password, changing the official contact e-mail address, or transferring funds. For such transactions, the application should require user interaction that would be extremely difficult for an automated program to duplicate. The three main options here are 1) use a CAPTCHA (typically a distorted image), 2) re-prompt the user for his password, or 3) use a previously-agreed-upon list of one-time passwords (which can be implemented via things like SecurID tokens).
You may be surprised to see that these countermeasures are not quite as effective as others we've discussed in this series. The main reason for this is that CSRF simply represents a client-side abuse of perfectly legitimate, designed-for Web 2.0 functionality. JavaScript is supposed to be able to read cookies, take actions for the users behind the scenes, etc. As I mentioned in last week's post, the best server-side countermeasure against CSRF is actually XSS prevention. Sure, there are many other attack vectors for launching CSRF attacks, but the majority of them involve compromises of the victim's PC or network connection, over which the web application developer has no control.
Next week's topic will be error handling--you might be surprised how much your error messages are revealing about the inner workings of your web application.
© 2011 CapTech Ventures, Inc. All Rights Reserved. Legal Notices.