So I was reading yesterday about the Cross-Site Scripting [attack against apache.org](https://blogs.apache.org/infra/entry/apache_org_04_09_2010. And it struck me that there might be an easy way to reduce or eliminate a lot of these attacks, using better isolation within the browser.
Essentially, my thought boiled down to this: Why, when I load a page in the browser, should that page have access to cookies from another server?
“But it doesn’t,” you might say. “The same-origin policy on cookies prevents one page from accessing another server’s cookies!” True. But if the malicious page manages to convince your browser to load a page from the target server, with its own cookie-stealing XSS code injected, then that malicious page, indirectly, has access to those cookies.
So let me rephrase the thought: Why should a web page be permitted to load a page (and worse, execute code) in an unrelated session?
The reason this is possible, to my not-very-XSS-savvy mind, is that the browser really has only a single Security Context (set of cookies, session information, login credentials, etc.). (It’s possibly more complex than this, but the description should hold at a basic level for this discussion). If you log into a web site, then that session information is stored at the browser level, and any window, tab, or frame within that current browser instance has access to that information. The same-origin policy can help to restrict what information a page loaded within those views can access, but the information is still there.
Which leads to my half-baked idea: Within the browser, isolate session information (the session’s Security Context) to only those pages directly related to that session.
This is best described by way of example [Note that I’m using Gmail and Twitter for simplicity, and don’t don’t mean to suggest that any XSS bugs currently exist in either service :) ]:
- I log in, fire up the browser, and open Gmail.
- Then, I open a second tab and log into Twitter.
- Later in the day, I click a link in a tweet for a “really awesome photo that you MUST see!”
- This opens in a 3rd tab (or possibly stays in the Twitter-focused 2nd tab), and contains what really is an awesome photo, but also includes a hidden iframe.
- That iframe fetches a page from Gmail that includes an XSS-injected script.
Now, here’s where my crazy idea and reality diverge. In the current situation:
- The browser fetches that page, and so it gains access to the browser’s global Security Context.
- The XSS script executes within that context, and since the page came from mail.google.com, also passes the same-origin test and therefore has access to my Gmail cookies.
- The script then sends my Gmail session information to the attacker.
- Now the attacker has my Gmail account. (boo!)
In my suggestion to isolate security contexts, here’s what happens:
- As before, the image page is loaded in either a 3rd tab or the tab containing the Twitter feed (depending on how I opened it).
- However, this tab is not the tab with Gmail, and thus does not have access to that Security Context.
- Therefore, when the invisible iframe is loaded, the script will not be able to steal my Gmail cookies.
- XSS attack thwarted. (yay!)
Now, you may be thinking, “bye-bye to multi-window web applications.” But that doesn’t need to be the case. There’s certainly nothing preventing the browser from letting a new tab or window inherit a tab’s security context when a user requests a new window from within that session. It’s only when the browser loads a new page, outside of a session’s same-origin boundaries, that the security context is forfeit.
Now, you may be thinking, “bye-bye to single-window web browsing.” But that doesn’t need to be the case. If you’re in (for example) Gmail, and click a link for an external site, that link can still load in the current tab. For the time being, that tab loses access to the Gmail context. But if the user, having read the linked page and chuckled appropriately at the joke within, then clicks “back” in the browser, the context should switch back to Gmail.
Would this have helped in the recent Apache attack? I think so. From what I understand, the attack went like this:
- User is logged into issue tracking application.
- User reads a bug report entered into that application by a malicious user.
- Report includes a link to an external site (in this case, a URL shortening service).
- That link redirects to another page from the bug tracking application, with XSS-injected code to steal the user’s credentials.
If the browser had the sort of context-limiting controls I’m envisioning, then the privileged session’s credentials would have been “lost” as soon as that external URL was requested, and not regained, even though it eventually redirected back to the originating site.
I will note, however, that if the URL had never “left” the site (was not obfuscated through a redirector service, but was clearly within the current context’s originating domain), then this approach wouldn’t have helped. But hopefully, then, the URL would have been ugly enough (containing embedded XSS code) that the user might’ve noticed the problem before clicking on it. Or better yet, it would have been rejected outright and never made it into the malicious user’s bug report in the first place.
To try to simplify, I’d propose starting with the following rules:
- Credentials (temporary cookies, current login sessions, etc. – the “Security Context”) shouldn’t be shared across browser views (windows, tabs, frames, invisible code-executing sandboxes, etc.).
- Within a view, the Security Context should not be shared with subsequently-loaded data with a different origin. This includes both full replacements of the current view (navigating to a new page) and subordinate views displayed or processed within the requesting page (iframes, etc.).
- A Security Context can be shared with a new tab, window, etc., when the user executes something within the session that requests that new view, provided it’s still within the same origin as the current site.
At the moment, I can only think of one real drawback (though I’m sure there are more). If, for example, you’re clicking on a bunch of different links, all of which lead to a site at which you have an account, then each of those clicks will pull up that content as a general, unauthenticated, first-time-visiting user. So if you have a current YouTube session open, and click on a YouTube link in a tweet, you’ll have to re-authenticate within the window that click opens in order to post any comments to the video. Perhaps the user could be permitted to drag such a link into a previously-authenticated session window, but unfortunately we then start to diminish the level of protection.
I’ve already been pointed at two projects that start to address this. The first, Caja, appears to be focused more on the secure development of a page in the first place, not as much on XSS vunerabilities. The second, CookiePie, has some of the features I suggest but is implemented in a manual fashion, with the goal being simultaneous use of a website by multiple different accounts. Neither solution really gets to the deep-in-the-browser XSS prevention that I think isolated Security Contexts could automatically provide.
So how crazy is this? Am I missing something obvious, or is it just chance that we haven’t tried to do this already? Or are there security features already present in browsers that do this, just not in a way I’ve noticed? It seemed, once I thought of it, absurdly simple, but then again, I thought of it, so that’s sort of a given.
I welcome any thoughts and criticisms (but still hope that there might be some value here).