Ajax Security, Part II: Attack Surface
Today I pick up from part 1 and discuss one of the challenges in Ajax security: attack surface.
Attack surface describes the points of entry that an attacker can abuse to compromise our application. When writing software with security in mind, we try to minimize attack surface to reduce the likelihood and the impact of successful attacks.
One straightforward way to reduce attack surface is to remove features that are not used or that pose too great a risk. If the code isn’t there, it can’t run so cannot be exploited. Another technique is to reduce the number and complexity of exposed entry points. In web applications, the most obvious entry point is the HTTP interface. This is where Ajax comes in.
Ajax & Attack Surface
Almost by definition, Ajax applications have an larger attack surface than their non-Ajax counterparts. The reason is simple: there are more server-side services exposed — i.e. more URLs. URLs are web applications’ exposed APIs, and Ajax apps make more of them available to end users.
Why? Ajax code needs to talk to something on the server, and usually that means exposing an additional functional URL. Take Google Suggest for an example. As you type in the search box, your browser is making HTTP requests behind the scenes to http://www.google.com/complete/search
(I’ve left off query parameters). When you submit the form, it goes to http://www.google.com/search as usual. So to introduce an Ajax feature, Google has exposed an additional entry point to their application(s).
Let’s use a more detailed example from an app that I’m working on, a course search. It’s simple enough, really, there are three basic pages: a form where students enter search criteria, a search results page, and a course detail page for each of the results. The URLs (minus query parameters) might look something like this:
/search/
/search/results
/search/detail
To be clear, I should add some reasonable query parameters:
/search/
/search/results?subj=english
/search/detail?courseid=34512
There are lots of ways we can introduce Ajax to these three pages that make it a little bit easier for the student to use. One is to remove the extra step of looking at course details on a new page. When a student clicks on a course title to get detail, instead of following the link to /search/detail, fire off an Ajax request to get the details and display them in situ, right on the results page. That way the student doesn’t have to keep going back and forth between search results and course detail, and can more quickly find course that interests her.
Here’s the question we face: what is the URL the Ajax code’s HTTP request? A few possibilities:
/search/detail?courseid=34512
. The problem here is that normally the response to this request will be an entire HTML page, complete with page headers, footers, navigation, etc. We don’t want all that stuff, we just want the course detail. This will not work.- Same URL as the first option, but with an additional HTTP header that indicates that this is an Ajax request. The server-side code looks for this header and responds accordingly. Prototype adds a custom header
X-Requested-With: XMLHttpRequest
, which is great but if you ever move away from Prototype you’ll either need to adjust your JavaScript to add the same header or change your server-side code to look for something new. Not necessarily an onerous task, but it’s worth considering. /search/detail?courseid=34512&output=json
. Here we’ve added a query parameter to tell the app to respond with JSON instead of a complete HTML page. Yahoo! does this for their REST web services. We could just as well have added output=xml our output=html to get XML or just the HTML to add directly to the DOM. This isn’t too bad a way to go, but we’ll want to find a way to avoid repeating the code that looks for the output parameter./search/detail-ajax?courseid=34512
. Use a different page name entirely. This way it’s easy to spot what’s going on, but it seems a little clumsy, especially as you add more Ajax-responding URLs. If you’re using a framework like Struts & Tiles, this is probably the easiest way to go, because you can point to the same Action but use a different set of Tiles for the output. On the other hand, your config file can quickly get pretty large.
If you choose option 3 or 4 (especially 4), you’re introducing new URLs to your app, thereby creating new points of entry and increasing your attack surface. Heck, even for option 2 you’re now relying on a new HTTP header. Never forget that HTTP headers are inputs to your application that need to be examined and carefully validated.
That’s just one example on a single page: I didn’t even mention all the possibilities for Ajax on the search form itself, dynamically creating or updating form fields based on selections made in the form. With the addition of each new Ajax feature, I’ve expanded the possible ways (permissible ways, really) of interacting with my application, increasing the amount of work my server-side code needs to do and the vigilance I need to bring to developing and reviewing the design and code.
Toolkits & Frameworks
I’ll write more about toolkits in a future entry, but I do want to mention now that server-side integration toolkits like DWR are good examples of increasing attack surface. With DWR, you can configure specific classes and methods that you want to expose via JavaScript, and DWR generates the JavaScript as a remote interface to your Java classes. I believe that CFAjax works in much the same way (heck, if memory servers it’s unapologetically a DWR port), as do many PHP Ajax frameworks. Every one of these interfaces is an extra API to your application that you are exposing, making your attack surface that much larger.
I haven’t researched this carefully, but some PHP Ajax frameworks have historically had bugs that permitted arbitrary code execution. Needless to say, this expands the attack surface a bit. :)
Client-Side Business Logic
The Ajax example that I gave earlier is a simple enhancement, but many Ajax apps, as opposed to Ajax-enhanced web apps, are moving more and more business logic to the web browser. This has subtle and not-so-subtle effects on state management, as I’ll explore in an upcoming entry. It also means that there’s a lot more code available for an attacker to play with.
I have encountered a strange, unstated expectation among some developers that once JavaScript code is sent out to a client, it’s untouchable. This seems to be especially the case with code generated by a framework. I’m not sure where this idea comes from, and I hope it crumbles with even the slightest examination, but it’s out there. And it’s flat-out wrong.
You don’t, for example, want to do things like send SQL queries in your Ajax requests. I swear, I actually saw this in a web app a couple weeks ago. I happened to have LiveHTTPHeaders open while I was surfing and saw a SQL statement scroll past. I investigated and sure enough, the server was executing whatever arbitrary SQL was sent to it from the browser.
The developer didn’t need Ajax, of course; it could just as easily have been a submitted HTML form. Still. Do not do this.
Another effect of moving more Ajax code onto the client is that there are new trust boundary concerns. As you know, input validation is essential to web application security. Software security in general, really. Any time data passes across a trust boundary, where you don’t entirely trust its origin or integrity — data submitted from a browser to a web server, for instance, or from a remote web service or a database to your application code — it is important to perform rigorous input validation. We are (hopefully) used to this. It ought to be second nature.
Let’s make this clear: we’re used to this on the server side, working in a trusted environment accepting untrusted input.
In Ajax apps, though, lots of data is being passed back and forth between client-side and server-side code. If there’s significant business logic running in the browser, it stands to reason that the browser should validate data that crosses a trust boundary on its way from the server. Here’s the thing, though: while on the server side our code runs in a trusted environment (or what we hope can be trusted), in a browser our code (or what we hope is our code) runs in an untrustworthy environment accepting untrusted input. In the past, we’ve more or less assumed that we could trust what came from the server. We learn from Amit Klein that we should not: the communication channel between the client and server is vulnerable and suspect.
The conundrum is of course that the code that does the validation is completely exposed on the client.
One thing to watch out for is blindly eval()
ing JavaScript. As I wrote in part 1, the server response to an Ajax HTTP request is typically either XML, JavaScript, or HTML. Nowadays the JavaScript is usually JSON, which looks like this:
{ "birthdays": [ {"name":'Abe Lincoln', "bday": "12 February 1809" }, {"name":"James Buchanan", "bday": "23 April 1791"}] }
Here’s a common way of using that response.
var obj = eval( '(' + res.responseText + ')' )
The risk here is that the server response may contain malicious code. If you blindly eval()
the response, you’ll immediately execute that code. You can mitigate the risk by using a JSON parser to at least ensure that it is indeed JSON.
var obj = resp.responseText.parseJSON();
This is of course far from a perfect solution. I confess that I am not sure how best to handle this. I need to do more thinking and more research. Pointers are welcome.
Web 2.0 Meets SOA
As more and more business logic pushes out to the browser, it is tempting to have that client code connect directly to web services in a service-oriented architecture (not that you need web services for SOA, but bear with me). JavaScript can do SOAP, so why not? The problem is that often those services are thin wrappers around 20-year old COBOL code. You don’t want to be exposing this to the world. Andrew van der Stock discusses this in interview for SearchAppSecurity.com.
I don’t have a problem with JavaScript making web services calls using SOAP, REST, XML-RPC, whatever. Keep in mind, though, that the browser environment cannot be trusted, so any validation or access control you’ve put in place there doesn’t do any good. If the value of the transaction is high — e.g. if it needs authentication at all — or if the code behind the service was written with the expectation of running in a completely trusted environment, then you’re far better off minimizing exposure by providing carefully controlled and monitored access to that code. Meaning, don’t allow anyone in the world to connect to it: limit access to code running on your server. If you want to use JavaScript to access the service, have it contact another service that’s tied into your access control mechanisms.
Resources & Further Reading
I’m not pulling all this out of thin air. Much has been written on the topic of Ajax security. A lot of it FUD, but there is some very worthwhile reading out there that I’ve used to inform my own research and writing. Here’s some of it:
- Mitigate Security Risks by Minimizing the Code You Expose to Untrusted Users
- An article about attack surface reduction by Michael Howard
- Ajax Security
- A presentation at AppSec by Andrew van der Stock.
- OWASP Guide 3.0
- The new version of the Guide to Building Secure Web Applications has a draft chapter on Ajax security.
- Ajax security resources
- At cgisecurity.com.
- del.cio.us/afongen/ajax+security
- Rather than just duplicate more links, I’ll send you to my del.icio.us links.
Coming Up…
Next time, we’ll look at another challenge in Ajax security: state management and access control.
17 Aug 2006 Sam