-2

I'm building a data processing system where users can submit hooks to execute on incoming data.

The hooks are untrusted and should execute in a sandbox with access only to a limited API that I expose – essentially like a DSL. Ideally, users write hooks in ES6 or Python. The code should preferably be executable from most runtimes, but definitely from Python.

From my perspective, I'm looking for this workflow:

  1. Users submits source code for a hook, which I compile and store
  2. I retrieve the compiled code and execute it
  3. Only calls to the predefined APIs have side-effects

What technologies do you recommend to achieve this?

These are the ideas I'm currently exploring:

  • Users write hooks in TypeScript, which is compiled to WebAssembly with AssemblyScript. I have not found any guidelines on the feasibility, security or overhead of running sandboxed WebAssembly in Python.

  • I check user-submitted code for imports and system/networking calls and then eval it in the current process. I have not found any guidelines on exactly which checks are necessary to securely eval Python and/or ES6.

1
  • 3
    Sharing your research helps everyone. Tell us what you've tried and why it didn't meet your needs. This demonstrates that you've taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. Also see How to Ask
    – gnat
    Commented Feb 21, 2019 at 22:21

2 Answers 2

3

Python has no security model that would allow you to safely execute untrusted code. If you want to execute untrusted Python code you need to apply operating system level safety measures, for example by running the code as a separate process in a highly restricted Linux container. For example you could prevent the code from accessing the file system of the host operating system, or can set a quota for CPU use for that container.

Javascript VMs take security much more seriously, because executing untrusted code is what browsers do all the time. Assuming you configure the VM correctly and that your API that you offer to the JavaScript code is secure, a JavaScript VM can be a very good approach for executing untrusted hooks. In particular, such VM-based isolation has a much lower latency and memory overhead than running a separate Python process in a container. This approach is also a core part of Cloudflare's serverless offerings.

The use of WebAssembly is irrelevant for the purpose of security, as long as you have a VM that can execute WebAssembly with suitable isolation guarantees.

Whatever approach you use, you fundamentally cannot check the code before execution to ensure that it doesn't do anything unsafe. Both JavaScript and Python are much too flexible to prevent effective static analysis, furthermore such program properties are provably unprovable (see also the Halting Problem). In any case, something like a regex that tries to find illegal imports is easy to circumvent for attackers. Please don't try to base your security models on a provably impossible approach.

6

If you do not trust the code, do not execute it - period.

If you wish to trust the code.

  • Constrain what is permitted as code
    • reject specific operations such as eval(...)
    • reject unbounded loops
    • reject code that modifies prototypes.
    • the list goes on...
  • Constrain where that code is executed.
    • preferably on a worker host which is on a separate and isolated network
    • that can only communicate with a Bastion Host in a DMZ Network
    • and the DMZ lies directly between your server core and the worker host.
  • Constrain what data and behaviour is available to the untrusted code

    • hint: as little as possible
    • nothing confidential or private if it is allowed to communicate across time.
      • If it can send a message/piece of data that it, or another untrusted code/entity will/may receive later. This allows collusion and data-leaks.
  • Constrain the execution time of the code.

    • if it does not finish within Xms it is terminated.
  • Constrain the priveleges of the code.
    • It should have none
    • the only exception is communicating with the bastion host
  • Constrain the effects of the code
    • any request to update/change data should be recorded so that reversing it is trivial.
    • any response or message sent to the user should be rate limited, and the user should have the option to silence/stop such behaviour.
    • any action performed on behalf of the user should be confirmed explicitly by the user
      • or at least in a similar context, have been formally acknowledged by the user as always okay.
      • such as: the user requested an email, triggering the untrusted code, and the user accepted this always.
      • should the script run when checking a profile, and it requests an email be sent, then the user should be approached for approval.

Hygiene is also important:

  • Regularly burn down all machines and rebuild from scratch.
  • Regularly patch all applications, platforms, libraries, etc...
  • Log all data/actions that untrusted code recieves, sends.
    • Similarly Log all actions that are performed on behalf of untrusted code, and any data they send/receive.
  • Design an untrusted code scanner to flag code that may have done, or could do bad things.
    • Ensure a human reviews the specific issues identified.
  • Employ Black Hats to specifically design untrusted code to break you system.
    • Ensure that you review both successful and failed samples
    • Figure out how to prevent successful examples from working
    • Figure out if you could have rejected failed examples earlier.

Your Answer

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

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