GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing healeycodes.com — Running Untrusted Python Code For a side-project, I needed to run some untrusted Python code. I would usually sandbox untrusted code inside a virtual machine. Either with a managed service like Fargate, Lambda, or Fly Machines; or if there were specific requirements (e.g. performance, cost) perhaps I would use a primitive like a Firecracker microVM or a V8 isolate. Since it's a side-project, I can do something less secure and more fun – like using a separate process with some limits applied to it. My understanding is that sandboxing untrusted code inside a separate process is something that used to be more common but, after many CVEs, it's now less common. You can go straight to trying to break out of my sandbox here: untrusted-python.vercel.app, view the source code, or keep on reading. I will update this post if (more like when!) someone finds a security hole in it. How it worksWhen the API receives some code, it spins up a new Python process. When the process starts, it has regular permissions and no resource limits. It then applies limits to itself (which can't be revoked) and then calls the guest code with The input is source code and the output is stdout/stderr. I have side-stepped the common mistake of building a sandbox in application land e.g. by removing access to parts of the runtime. In Python, removing builtins seems like it will stop people being able to access the system:
But program languages (and especially dynamic programming languages) allow you to do funky things, like walking up a class tree and dancing across frames:
seccompWhen you hand out virtual machines, you don't need to hide parts of the runtime. If a user decides to start deleting system files, that's fine! The output of the sandbox will be broken/undefined/errored but it doesn't matter. Everything is discarded in the end. When using a separate process as a sandbox, you need something that sits between the program and the system – and when a user tries to delete a system file they should hit a meaningful error like For my sandbox, the layer between the process and the system is seccomp (secure computing mode) – a feature of the Linux kernel restricts a process from calling any system call apart from You can also set seccomp filters to specify which system calls are allowed or not allowed, and what error code should be returned. That's what I do:
This method of filtering system calls isn't super flexible e.g. you can't specify which files are allowed to be read or written to – just file descriptors. Note: I avoided namespaces/cgroups to make it a little easier to ship something. setrlimitAs well as system calls, you also need to worry about resources. My sandbox runs on the smallest Fly Machine size so there aren't many resources to go around. If someone sends up You can limit wall clock time from the outside with a timeout like this:
But during that timeout, what if some code tries to fill all available memory? Or writes GBs of data to stdout/stderr? setrlimit is one of the answers for this – a system call that allows a process to set resource limits on itself/its child processes. There are different limits available but I picked out the ones I'm concerned about. Python lets you call into this from the standard library module resource.
I suspect I'm missing an important limit here. If some Python code is able to monopolise a system resource in a way that the API or another sandbox process is affected I would consider it a security hole. Let me know if you can break something here. DDoSing is cheating! More on sandboxesAs part of my Python sandbox research, I read about The failure of pysandbox:
Which prompted me to just wrap it in seccomp. Pypy's sandbox seems pretty promising if you need to productionize a Python sandbox (and, for some reason, you can't use virtual machines). A few months ago I wrote about sandboxing JavaScript code and built a TypeScript sandbox with Deno. Shortly after publishing it, I received a few bug bounties for reporting sandbox-related exploits in Val Town's runtime. They fixed these issues by shipping Restricted Library Mode. After poking at it, it seems like an additional isolation implemented with Workers. JavaScript (and its various runtimes) have features that make sandboxing/isolation a little easier than Python because JavaScript always has sandboxes in mind (i.e. web browsers). One idea I'm noodling around with is sandboxing SQLite at the process level – using a mixture of its run-time limits, and using ptrace to restrict access to any file that isn't the main database file or one of SQLite's nine types of temporary files. |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |