7

tl;dr Can I execute un-trusted scripts on an iframe safely?

Back story:

I'm trying to make secure JSONP requests. A lot of older browsers do not support Web Workers which means that the current solution I came up with is not optimal.

I figured I could create an <iframe> and load a script inside it. That script would perform a JSONP request (creating a script tag), which would post a message to the main page. The main page would get the message, execute the callback and destroy the iframe. I've managed to do this sort of thing.

function jsonp(url, data, callback) {
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    document.body.appendChild(iframe);

    var iframedoc = iframe.contentDocument || iframe.contentWindow.document;
    sc = document.createElement("script");

    sc.textContent = "(function(p){ cb = function(result){p.postMessage(result,'http://fiddle.jshell.net');};})(parent);";
    //sc.textContent += "alert(cb)";
    iframedoc.body.appendChild(sc);
    var jr = document.createElement("script");

    var getParams = ""; // serialize the GET parameters
    for (var i in data) {
        getParams += "&" + i + "=" + data[i];
    }

    jr.src = url + "?callback=cb" + getParams;
    iframedoc.body.appendChild(jr);
    window.onmessage = function (e) {
        callback(e.data);
        document.body.removeChild(iframe);
    }

}

jsonp("http://jsfiddle.net/echo/jsonp/", {
    foo: "bar"
}, function (result) {
    alert("Result: " + JSON.stringify(result));
});

The problem is that since the iframes are on the same domain, the injected script still has access to the external scope through .top or .parent and such.

Is there any way to create an iframe that can not access data on the parent scope?

I want to create an iframe where scripts added through script tags will not be able to access variables on the parent window (and the DOM). I tried stuff like top=parent=null but I'm really not sure that's enough, there might be other workarounds. I tried running a for... in loop, but my function stopped working and I was unable to find out why.

NOTE:

I know optimally WebWorkers are a better isolated environment. I know JSONP is a "bad" technique (I even had some random guy tell me he'd never use it today). I'm trying to create a secure environment for scenarios where you have to perform JSONP queries.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 1
    What if you set the `document.domain` of the `iframe` as something else? Therefore, they won't be able to talk to other without `postMessage`. Just an idea. I remember that you can let them talk if you set them both to the same value, so I wonder if doing the opposite would yield the opposite. I'm messing around with some stuff in the fiddle; this is an interesting question, and I like your approach :) – Ian May 22 '13 at 00:35
  • @Ian can I change `document.domain` myself in the iframe's code? If I do, can I guarantee that it won't be changed back by the malicious code? – Benjamin Gruenbaum May 22 '13 at 00:37
  • Yeah, I'm pretty sure you can. Well, you can change the value in the parent page as well to something random/unique too, then it would be pretty hard for the iframe script to guess and match it. I think I should've thought through the whole scenario before commenting, but it may be something you could toy with. I forget the point of setting it and what it actually changes, I just remember seeing it being synced so that a window and iframe *could* talk (by what means, I can't remember) – Ian May 22 '13 at 00:41

2 Answers2

1

tl;dr no

Any untrusted script can steal cookies (like a session id!) or read information from the DOM like the value of a credit card input field.

JavaScript relies on the security model that all code is trusted code. Any attempts at access from another domain requires explicit whitelisting.

If you want to sandbox your iframe you can serve the page from another domain. This does mean that you can't share a session or do any kind of communication because it can be abused. It's just like including an unrelated website. Even then there are possibilities for abuse if you allow untrusted JavaScript. You can for instance do: window.top.location.href = 'http://my.phishing.domain/';, the user might not notice the redirect.

Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • 1
    How? It works [with WebWorkers](http://stackoverflow.com/a/16660146/1348195) , what is the core problem preventing me to delete all references to the parent window inside the iframe? I tried doing so and didn't succeed, but I'd like to be convinced it's impossible. Can you give me an example of something I can't set to null here http://jsfiddle.net/Xwu3S/ before the second script tag is executed which will compromise security? In your example, can't I do `window.top = null;` ? – Benjamin Gruenbaum May 22 '13 at 00:11
  • I don't know man, you are asking 3 questions in one. What question are you referring to? – Halcyon May 22 '13 at 00:14
  • I'm asking just one thing: "Why is the answer no?", your argument is that the script might steal information, you show it by the example `window.top. ...`. I'm saying I can set `window.top` to null. I'd like to see why that would not help me. I'm not saying you're wrong, I just want to understand what the inherent issue is. – Benjamin Gruenbaum May 22 '13 at 00:16
  • I don't know about that .. there could be ways around it. For one you could use `window.parent` till you reach the top. You could perhaps block that as well but maybe there's a way around that too. At the very least you can do `window.location.href` and send the user anywhere. How are you going to block that? It becomes a cat and mouse game that you will lose because the mouse only has to score once. – Halcyon May 22 '13 at 00:21
  • Intereting points. For all I care the external JSONP service can freeze the browser, they can navigate the iframe away, they can move stuff in it. It's an invisible iframe, they can easily run `while(true){}` which would hang the browser since an iframe runs on the same thread. The only thing I really care about is security. I don't want to let the iframe to execute the DOM, or the JavaScript variables in my main window in any way. As long as I keep users' data safe, that's a win for me. – Benjamin Gruenbaum May 22 '13 at 00:26
  • I can not guarantee that there isn't a way to mess with the top window. I can guarantee that scripts from another domain can't read anything from another domain, parent or child. A `while(true){}` implementation has a serious impact on usability though. Do you have any more hidden use cases? If you had put this information in your question I would have been able to give a better answer. – Halcyon May 22 '13 at 00:30
  • I would not like to use JSONP, but if I have to I'd like it to be a potential usability problem and not a potential security problem. Can I create a frame which will be treated as cross-domain which will work on older browsers (with code)? I didn't find a way. Frames have to be on the same domain, I would have put this in the question if cross-domain frames were an option. Thanks by the way. – Benjamin Gruenbaum May 22 '13 at 00:35
  • You can do cross-domain/frame communication, but not in older browsers (IE7-). JSONp itself is no security issue, it's running untrusted 3rd party code that is the problem. – Halcyon May 22 '13 at 00:38
1

You can't really delete the references, setting null will just silently fail and there is always a way to get the reference to the parent dom.

References like frameElement and frameElement.defaultView etc. cannot be deleted. Attempting to do so will either silently fail or throw exception depending on browser.

You could look into Caja/Cajita though.

Esailija
  • 138,174
  • 23
  • 272
  • 326