Вы находитесь на странице: 1из 10

02 OCT 2014

jQuery and Cross-site Scripting

I ran into an interesting issue yesterday related to the use of jQuery and a potential XSS (cross-site scripting) vulnerability. It was an easy mistake to make, and one I unfortunately see (and occasionally make myself) all too often.

So let’s break things down and prevent this bug from coming up again!

The Attack

A casual reader reported the attack. It seems they could append some seemingly arbitrary text to a visible URL and cause the page to trigger an alert. Digging deeper, they also pointed out that a similar attack could redirect the page to any URL they wanted.

For example,

https://eamann.com/#"><img src=M

onerror=alert('test');>

would cause the page to post an

alert of “test.”

  • 1 Similarly,

https://eamann.com/#"><img

src=M
src=M

onerror=window.location.replace('http://facebook

would force the browser to automatically redirect to Facebook.

On the surface, this seems like a non-issue – an attacker could display an alert or force a page to redirect.

2

The attack itself, though, goes much deeper and has much more nefarious implications. Essentially, this vulnerability allows an attacker to execute any arbitrary JavaScript they want, from the user’s own context. An alert or redirect to Facebook is trivial – POSTing the current user’s cookies to a 3rd-party site, though, is not.

If a site is vulnerable to this attack, I could craft a URL that would grab the contents of the page’s cookies and send them to anyone I desire. Once I have your cookies, I can spoof your logged-in

session even if you logged in via HTTPS.

Doesn’t seem like a non-issue any more, does it?

The Cause

It turns out, a 4-line script on the page was at fault. Some fancy tab

switching going on in an internal page required JavaScript to read the current URL hash (the component after the # sign) and use it in an element selector to help the page know which tab to load. Like many developers, jQuery was used to build a quick proof-of- concept of the feature – and ended up being shipped to production in finished code.

The prototype looked something like:

if ( $( '.class .' +

window.location.hash + ' ul' ) ) { ...

On the surface, this looks just fine. Until, that is, you remember what jQuery does behind the scenes with selectors. First, jQuery will attempt to parse the selector as a selector – the intended use case. If the selector fails to validate, jQuery assumes the string passed in is instead a block of HTML, and subsequently attempts to parse it.

.class ."><img src=M onerror=alert('test');> ul

will force jQuery to attempt to create an image tag, with a broken source attribute, and an error handler containing the attacker’s desired script package. Since the source is broken (in this case just an “M”), the error handler triggers immediately and executes whatever script the attacker wants.

It could display an alert. It could redirect the page. It could grab

the browser’s cookies (including your authenticated session cookie) and send them to a remote party.

It’s a pretty significant bug, and was created merely because someone failed to recognize the security implications of a quick proof-of-concept script and shipped it to production as-is.

The Solution

Instead of jQuery, the selector should be parsed using native DOM

methods.

document.querySelectorAll()

serves the same

purpose here. Unlike jQuery, it throws a syntax exception if you attempt to pass the broken image tag used in the attack – an exception that is easily caught and discarded.

if ( 0 < document.querySelectorAll('.class .' +

window.location.hash + ' ul' ).length ) { ...

serves the same purpose in this conditional. Wrapped in a try/catch block, it completely plugs the hole and keeps the site’s naive selector logic from opening a door to an attacker.

The original site hosting the vulnerable code has been patched,

and everyone involved learned a helpful lesson

  • 3 about their code:

never trust any form of user input, even if it’s coming from an

allegedly trustworthy source. Referencing properties on the global

window object feels safe because it’s not coming directly from the user; just always keep inShellshock bug reminds us just how important it is to always identify the source of parameters and, even if they’re a trusted source, to be skeptical of what data is passed in regardless. Notes: 1. Note: I’m using my domain in these examples, but this site was not affected by this vulnerability. 2. Typically, redirections are a negative thing. But if you’re clicking on a link like this in the first place, you should be aware that something is up. 3. never fault anyone for making a mistake like this. After I seeing this bug, I went back through my own code and found numerous examples that might present similar vulnerabilities. Coding is a collaborative and iterative process, so the fact that someone caught the bug and that it was fixed quickly speaks volumes about the success of the development team involved. I measure success not in the amount of bug-free code produced, but in a team’s overall ability to learn from and correct their mistakes. " id="pdf-obj-4-2" src="pdf-obj-4-2.jpg">

window object feels safe because it’s not coming directly from the

user; just always keep in mind where the values in those properties come from.

The recent discovery of the bash-related Shellshock bug reminds us just how important it is to always identify the source of parameters and, even if they’re a trusted source, to be skeptical of what data is passed in regardless.

Notes:

  • 1. Note: I’m using my domain in these examples, but this site was not affected by this vulnerability.

  • 2. Typically, redirections are a negative thing. But if you’re clicking on a link like this in the first place, you should be aware that something is up.

  • 3. never fault anyone for making a mistake like this. After

I

seeing this bug, I went back through my own code and found

numerous examples that might present similar vulnerabilities. Coding is a collaborative and iterative process, so the fact that someone caught the bug and that it was fixed quickly speaks volumes about the success of the development team involved.

I

measure success not in the amount of bug-free code

produced, but in a team’s overall ability to learn from and

correct their mistakes.

Share this:

 Print
Print
 Facebook
Facebook
 Twitter
Twitter
 Google
Google

Filed Under: Technology Tagged With: javascript, jQuery, XSS

Comments

Just a quick note on this particular issue, it was fixed by jQuery in version 1.7, released in November 2011. The string must now start with HTML to be interpreted as HTML, otherwise it is interpreted as a selector (and in this case it would throw an error).

I agree with the general point tho. It’s a bad idea to send raw unfiltered URL or user input into JavaScript APIs, you don’t know where it’s been. jQuery can’t fix the general case because it’s a feature to inject HTML via the jQuery API. It’s only a bug when you let untrusted data inject HTML.

I’ll have to dig a bit more into why exactly jQuery was behaving this way, but I can confirm that v1.11.1 was running on the affected site.

I just did a quick spot test. It appears that jQuery 1.9 does not exhibit this behavior (indicating that the bug was, in fact, fixed). But jQuery 1.11.1 does. I verified against 3 different sites in production, and one completely clean testbed.

It looks like this is a regression.

This jsbin shows everything working okay:

http://jsbin.com/wupudu/1/edit If you switch that to 1.4.2 for example the alert will show.

If you’re using the jquery-migrate plugin it would put back the hole because otherwise selectors and HTML interpretation work differently and break many older pages.

Nice catch. The common element among these sites was the jquery-migrate plugin. I wonder if it’s worth fixing the issue in the plugin, or if we should just focus instead on not needing the plugin in the first place (I lean towards the latter option).

Dave, I believe that the selector injection issue was fixed in jQuery 1.9, not 1,7, as the following upgrade guide suggests:

I’m no jQuery or JS expert, but I believe the normal workaround to prevent this is to use the .find() method instead of dropping the hash straight into the selector.

So, instead of $( ‘.class .’ + window.location.hash + ‘ ul’ ) you would do something more like $( ‘body’).find( ‘.class .’ + window.location.hash + ‘ ul’ )

is this the same issue:

?

Yep. Though of note, the newer version of jQuery are much safer in terms of not being a sink (they do some sanitization early to prevent these kinds of errors). But certain features of jQuery Migrate reintroduce the issue.

Leave a Reply

Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-11" src="pdf-obj-9-11.jpg">
Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-13" src="pdf-obj-9-13.jpg">
Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-15" src="pdf-obj-9-15.jpg">

! "

Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-20" src="pdf-obj-9-20.jpg">
Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-22" src="pdf-obj-9-22.jpg">
Yep. Though of note, the newer version of jQuery are much safer in terms of notReply Leave a Reply ! " # $ % & Search this website … Copyright © 2016 · Whitespace Pro Theme on Genesis Framework · WordPress · Log in " id="pdf-obj-9-24" src="pdf-obj-9-24.jpg">
&
&

Search this website