Running untrusted code in SaaS world

Photo by Fahrul Razi on Unsplash

Running untrusted code in SaaS world


2 min read

Developing SaaS applications which run untrusted code provided by user is a tough problem to solve. This is particularly hard when developing a SaaS service with PLG sales notion.

Traditionally when enterprises sell software to other enterprises, there is a boundary of trust guided by terms of services. The product sold will have certain level of isolation to keep the customer's data separate and allow product customization via code or configuration. Enterprises know who their users are, customers are bound by the agreements in place and there is a certain level of trust.

Compare this to a SaaS offering sold direct to any customer. Anyone can simply fill a form and get access to the product. No credit card needed and no email to validate. There is a rush to get the user's attention and land the user directly to the product home page with zero friction. There is zero trust in this case. If this product were to support running user provided custom code to provide certain functionality, this is a huge problem from security perspective.

Here are few things I learnt along my journey to support Javascript execution:

  • Any feature that supports running untrusted user code, must assume that the code being executed is unsafe and add appropriate guardrails.

  • Provide a sandboxed environment so the untrusted code can load only certain whitelisted APIs or libraries. e.g. you may not need to allow access to file system. VM2 is a great library that provides sandboxing for Node processes and similarly Rhino/Nashorn has flags that can be set to restrict use of certain APIs.

  • Leverage Docker so the untrusted code can be executed in an isolated environment. Running untrusted code directly on a host can expose your infrastructure to the code.

  • Setup a communication mechanism between your core service and your code execution environment. The strategies can vary widely - write to DB directly with results, PubSub, HTTP etc.

  • Establish a "Setup and teardown" mechanism to ensure the environment gets reset for each execution of code.

  • Deployment strategies need to be defined to ensure updates can be applied easily without downtime or loss of data between the systems during upgrades.

The list is not exhaustive but it sheds light on core principles for building a feature that runs untrusted code.