33

I need to serve user-submitted scripts on my site (sort of like jsfiddle). I want the scripts to run on visitors browsers in a safe manner, isolated from the page they are served on. Since the code is submitted by users, there is no guarantee it is trustworthy.

Right now I can think of three options:

  • Serve the user-submitted content in an iframe from a different domain, and rely on the same-origin policy. This would require setting up an additional domain which I'd like to avoid if possible. I believe this is how jsfiddle does it. The script can still do some damage, changing top.location.href for example, which is less than ideal. http://jsfiddle.net/PzkUw/
  • Use the sandbox attribute. I suspect this is not well supported across browsers.
  • Sanitize the scripts before serving them. I would rather not go there.

Are there any other solutions, or recommendations on the above?

Update

If, as I suspect, the first option is the best solution, what can a malicious script do other than change the top window location, and how can I prevent this? I can manipulate or reject certain scripts based on static code analysis but this is hard given the number of ways objects can be accessed and the difficulty analysing javascript statically in general. At the very least, it would require a full-blown parser and a number of complex rules (some, but I suspect not all, of which are present in JSLint).

5
  • 2
    I think your sub-domain idea is best. I've looked into this question myself and couldn't find a better solution. I once played with loading the scripts dynamically through XmlHttpRequest and running eval -- I don't recall why I dropped it, though. Commented Aug 31, 2012 at 6:22
  • Related: stackoverflow.com/questions/958997/… Commented Sep 22, 2012 at 3:20
  • I agree with @JeremyJStarcher. Regardless, do you have a chance to control certain JS elements, window being the most important? If so, I would try to make it reject certain requests made from the iframe but I'm not sure how easy that would be. Commented Sep 23, 2012 at 2:12
  • 1
    As of now (more than 2 years after you asked your question) browser support for sandbox is pretty good. Commented Oct 15, 2014 at 3:14
  • iframe + sandbox seems to be the recommended solution: medium.com/zendesk-engineering/… Commented Mar 19, 2018 at 19:59

4 Answers 4

27
+100

Create a well defined message interface and use JavaScript Web Worker for the code you want to sandbox. HTML5 Web Workers

Web Workers do not have access to the following DOM objects.

  • The window object

  • The document object

  • The parent object

So they can't redirect your page or alter data on it.

You can create a template and a well defined messaging interface so that users can create web worker scripts, but your script would have the final say on what gets manipulated.

EDIT Comment by Jordan Gray plugging a JavaScript library that seems to do what I described above. https://github.com/eligrey/jsandbox

Sign up to request clarification or add additional context in comments.

9 Comments

Sounds like a good alternative but might be too restrictive. Nevertheless +1 for the idea.
Good suggestion. My concern is that older browsers don't support this. Also, can't web workers make AJAX requests (meaning I'd still need to serve from a different domain)?
@Andrew - What scenario do you see a web worker having AJAX capabilities being an issue? If you're worried about the worker script impersonating the user on your own domain, you could just use cookie-less sessions and avoid that problem. As for browser compatibility, yep, IE9 and below don't support HTML5.
Incidentally, you might want to add a link to jsandbox, a sandboxing library that uses this approach.
Beware you can still do a lot of mess using this technique! You can steal cookies, you can fetch() any url address so you can do CSRF attacks (add me as admin using your admin session) and so on. It is NOT secure at all.
|
5

Some ideas of tools that could be helpful in your application - they attack the problem from two different directions: Caja compiles the untrusted JavaScript code to something that is safe while AdSafe defines a subset of JavaScript that is safe to use.

Caja

Caja

The Caja Compiler is a tool for making third party HTML, CSS and JavaScript safe to embed in your website. It enables rich interaction between the embedding page and the embedded applications. Caja uses an object-capability security model to allow for a wide range of flexible security policies, so that your website can effectively control what embedded third party code can do with user data.

AdSafe

AdSafe

ADsafe makes it safe to put guest code (such as third party scripted advertising or widgets) on a web page. ADsafe defines a subset of JavaScript that is powerful enough to allow guest code to perform valuable interactions, while at the same time preventing malicious or accidental damage or intrusion. The ADsafe subset can be verified mechanically by tools like JSLint so that no human inspection is necessary to review guest code for safety. The ADsafe subset also enforces good coding practices, increasing the likelihood that guest code will run correctly.

2 Comments

I have looked into AdSafe. It is designed for scripts that aren't in frames and is therefore very restrictive. For example, you can't do a[i] = 1. I think the restrictions may scare the majority of legitimate users. Will have a look at Caja.
You can do a[+i] = 1 though. (This is because when someone writes a=window; i='ev'+'al' then a[i] would mean eval and it would be impossible to statically eliminate that possibility.) But you are right that it may be too restrictive for your needs.
4

As mentioned, the sandbox attribute of the iframe is already supported by major browsers, but I would additionally suggest a mixed solution: to start a web-worker inside the sandboxed iframe. That would give a separate thread, and protect event the sandboxed iframe's DOM from the untrusted code. That is how my Jailed library works. Additionally you may workaround any restrictions by exporting any set of functions into the sandbox.

2 Comments

Question about your library, why do you run the code in a sandboxed iframe AND a web worker? Wouldn't it be safe enough to have a normal iframe, and then run the untrusted code in a web worker? The web worker could not access the dom of the iframe, or get it to do anything else really, except for what you do in your onmessage callbacks. Am I correct?
Well, not exactly. Initially it ran the code in a web worker only, then I was explained that it is not enough, since a worker still has access to some local origin instances, like local storage, indexeddb, etc. So I had to put a worker in a sandboxed iframe. A worker was still useful to obtain a thread and prevent locking the page with an infinite loop. But later on it appeared that many browsers prohibit running worker in an iframe under https, so now the library only starts a worker if possible, otherwise the code simply runs in an iframe.
-4

If you want to sandbox some piece of code by removing it's access to say the window, document and parent element you could achieve it by wrapping it in a closure where these are local empty variables:

(function(window, document, parent /* Whatever you want to remove */){
  console.log(this);      // Empty object
  console.log(window);    // undefined
  console.log(document);  // undefined
  console.log(parent);    // undefined
}).call({});

Calling it with an empty object is important because otherwise this will point to the window object

1 Comment

console.log(function(){return this;}()); :P

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.