1

I have a tough issue to solve, and I've done a lot of research and I don't think I've found a perfect answer yet.

I am making a game, and I plan on writing a pretty big level editor. In my head, this level editor might be powerful enough for me to write the entire game in. That means, I'd only need to add new features to that game editor to add features to the game. So far, so good. However, my game is written in Javascript, and I plan on making the level editor inside the same game engine, so I can actually ship it with the game, and let players make their own levels. Inside that level editor, I plan on adding scripting features to extend the capabilities of the game.

This means, I'd like the level editor to be able to run Javascript. So I want users to be able to write and run JS in the game, and also be able to share their levels and have other people play these levels.

Now, this sounds really bad. This is me basically allowing users to do XSS through my game. I've thought of many ways, and basically, I haven't found any solution to allow users to do this safely.

I want them to be able to access a handful of JS objects I give them and NOTHING ELSE. I don't want them to be able to access ANY other variable, like window, navigator, console etc. I only want them to be able to access my game's runtime object and whatever utils functions I give them. So, how can I accomplish that safely?

4
  • Is this running in the frontend? Commented Jan 5, 2021 at 22:40
  • Why don't you put your unaccessible variables in a function scope, and the editable in the global scope? Check this out: repl.it/talk/learn/… Commented Jan 5, 2021 at 23:57
  • @JulianKleine yes Commented Jan 6, 2021 at 10:52
  • @Clvckl3s because there are always ways to trick the scope and go to the global one. I can't just expect the scope to protect me, that's a terrible idea. Commented Jan 6, 2021 at 10:57

1 Answer 1

2

If the custom script runs on the client, the first step would be to at least run it inside a sandbox rather than on the parent page. You could create a web worker from the user-provided script and then pass a message to the worker including only the information you want it to be able to use. Workers run in a separate environment and do not provide access to the original page, nor to many of the built-in browser objects. But some built-in objects will still be accessible, like window.console and window.fetch. If that's acceptable, you could run the user's script in the worker, and have it post a message back to the original page, and then discard the worker.

You might be able to do shenanegans like using with and new Function in an attempt to prevent the user from accessing certain global properties, but that'd be tedious and I wouldn't be confident in being able to do is successfully.

A better approach would be to run the script in a truly isolated world - send the code to your server, and use Node to run the script with vm.runInNewContext, and have the server send the results back to the client. This provides the isolation you need, because:

When you use this function it creates a VERY limited context. You'll need to pass anything you want into the sandbox object which become global objects. For example: You will need to include console in the sandbox object if you want your untrusted code to write to the console.

That said, in most cases, I'd first consider if running the user's JS is absolutely necessary. I'd much prefer allowing the user to customize behavior by defining a text format that they input text into. But if you need to provide unlimited flexibility given the game, creating such an (essentially) programming language could be quite time-consuming if the game is complicated.

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

1 Comment

That got me thinking. I found a browserified version of vm that uses sandboxed iframes, and wrote this: jsfiddle.net/vuj8wdkb Now I'm wonder if there are ways for the code I run in the iframe to bust out of its sandbox. I'm looking into it

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.