Python - Python Sandboxing and Secure Execution

Python sandboxing refers to the practice of executing code in a controlled and restricted environment so that untrusted or potentially harmful code cannot damage the system, access sensitive data, or perform malicious operations. This concept is especially important in applications such as online code editors, plugin systems, automation platforms, and any system that allows users to submit and run their own Python code.


1. Why Sandboxing is Necessary

Python is a powerful and flexible language that allows access to system-level resources such as files, network connections, memory, and operating system commands. If untrusted code is executed without restrictions, it can:

  • Delete or modify files

  • Access confidential data

  • Execute system commands

  • Consume excessive CPU or memory (Denial of Service)

  • Open network connections for malicious purposes

Because of this, sandboxing is required to limit what the executed code can do.


2. Challenges in Python Sandboxing

Creating a secure sandbox in Python is difficult due to the dynamic and introspective nature of the language. Some challenges include:

  • Python allows runtime modification of objects (reflection and introspection)

  • Built-in functions like eval(), exec(), and __import__() can bypass restrictions

  • Access to internal objects via attributes such as __class__, __bases__, and __subclasses__

  • Ability to reconstruct restricted functionality indirectly

For these reasons, simple approaches like removing certain built-ins are not fully secure.


3. Basic Sandboxing Techniques

a) Restricting Built-in Functions

You can limit access to dangerous functions by controlling the __builtins__ dictionary.

Example:

safe_globals = {
    "__builtins__": {
        "print": print,
        "len": len,
        "range": range
    }
}

exec("print(len(range(5)))", safe_globals)

This prevents access to functions like open, eval, or exec. However, this is not fully secure because Python provides ways to bypass these restrictions.


b) Using Restricted Execution Libraries

Some libraries attempt to provide safer execution environments:

  • RestrictedPython (used in Zope)

  • PyPy sandbox (runs Python in a restricted interpreter)

These tools limit available operations but still require careful configuration.


c) Limiting Resources

To prevent abuse, resource limits can be applied:

  • CPU time limits

  • Memory limits

  • File system access restrictions

In Linux, this can be done using resource module:

import resource

resource.setrlimit(resource.RLIMIT_CPU, (1, 1))  # limit CPU time

4. Process-Level Isolation (Recommended Approach)

Instead of relying only on Python-level restrictions, a more secure approach is to isolate execution at the operating system level.

a) Running Code in a Separate Process

Untrusted code can be executed in a separate process with restricted permissions.

  • Use subprocesses

  • Kill the process if it exceeds limits

b) Containers

Technologies like Docker allow you to run code in isolated environments with:

  • Limited file system access

  • Restricted networking

  • Controlled CPU and memory usage

c) Virtual Machines

A stronger isolation method where code runs in a completely separate OS environment.


5. Advanced Sandboxing Techniques

a) AST-Based Code Filtering

Instead of executing raw code, parse it using the ast module and allow only safe constructs.

Example:

  • Disallow imports

  • Block attribute access

  • Restrict function calls

This approach helps prevent execution of dangerous operations before runtime.


b) Whitelisting Instead of Blacklisting

Rather than blocking known dangerous features, only allow explicitly safe operations.

For example:

  • Allow arithmetic operations

  • Allow simple loops

  • Disallow file handling and networking


c) Seccomp and OS-Level Security (Linux)

Advanced systems use kernel-level restrictions such as:

  • Seccomp (restrict system calls)

  • AppArmor or SELinux policies

These prevent even low-level system misuse.


6. Common Pitfalls

  • Removing __builtins__ is not enough

  • Blocking import does not stop indirect imports

  • Attackers can exploit object inheritance chains

  • Sandboxing within the same process is inherently unsafe

Because of these issues, pure Python sandboxing is generally considered unreliable for high-security needs.


7. Best Practices

  • Never execute untrusted code in the main application process

  • Use containers or virtual machines for strong isolation

  • Apply strict resource limits

  • Validate and analyze code before execution

  • Log and monitor execution behavior


8. Real-World Use Cases

  • Online coding platforms (e.g., code judges)

  • Plugin systems in applications

  • Educational tools that run student code

  • Automation systems with user-defined scripts


Conclusion

Python sandboxing is a complex but essential concept when dealing with untrusted code. While basic restrictions can reduce risk, they are not sufficient for complete security. The most reliable approach combines code-level restrictions with operating system-level isolation, ensuring that even if malicious code runs, it cannot harm the host system.