As software engineers we sometimes have to execute some arbitary code that we found or some jar in (Java) case to figure out what its doing. But copy pasting some code in a system directly is a poor security practice thus having a sandbox environment is a powerful tool for ensuring code isolation, enabling secure execution by restricting access to core libraries and system resources. This blog walks through creating a robust sandbox environment in Java using custom class loaders and security policies—tools that are especially valuable for those working on plugins, user-generated scripts, or controlled environments for evaluating code.
The Basics of Java Sandboxing
Sandboxing in Java involves creating a restricted environment where code can execute safely, isolated from the rest of the system. It allows developers to control which resources are accessible, monitor the classes being loaded, and intercept potentially malicious behaviors. Key components in sandboxing include:
- Class Loaders: Responsible for loading classes into the JVM, custom class loaders enable control over which classes and packages can be accessed.
- Security Policies: Define the permissions granted to loaded code, restricting access to system properties, file systems, network resources, and other sensitive areas.
Modern Java Sandboxing Approaches
As of Java 21, there are several approaches to implement secure sandboxing:
- Java Platform Module System (JPMS) - Using strong encapsulation
- Process Isolation - Running untrusted code in separate JVM processes
- Custom ClassLoaders with Resource Limits - Our focus for this article
System Architecture
┌────────────────────┐
│ Client Code │
└─────────┬──────────┘
│
┌─────────▼──────────┐
│ Sandbox Runner │
├───────────────────-┤
│ - Resource Limits │
│ - Class Validation │
│ - Access Control │
└─────────┬──────────┘
│
┌─────────▼──────────┐
│ Custom ClassLoader │
└────────────────────┘
Implementing the Custom Class Loader
Our custom class loader, SandboxClassLoader, extends URLClassLoader and overrides the loadClass method to control which classes can be loaded. This ensures that only classes from allowed packages are accessible.
| |
| |
This policy restricts the code from performing any unauthorized actions, such as accessing the file system or network, by granting only the permissions explicitly added.
Running Code in the Sandbox
With the custom class loader and security policy in place, we can now execute untrusted code securely.
| |
This SandboxRunner class sets up the security manager and policy, compiles the untrusted code, loads it using the SandboxClassLoader, and invokes its main method.
Testing the Sandbox
Let’s test the sandbox with code that should be allowed and code that should be restricted.
| |
Real-World Applications
- Online IDEs: Running user-submitted code safely
- Plugin Systems: Loading third-party extensions
- Educational Platforms: Executing student assignments
- Code Interview Platforms: Running candidate solutions
Performance Considerations
- Process creation: ~100ms overhead
- Memory usage: Configurable per instance
- Compilation time: ~50ms for simple classes
Conclusion
This sandbox implementation provides:
- Process isolation for maximum security
- Configurable resource limits
- Automatic cleanup of temporary files
- Comprehensive error handling
- Support for both source files and JARs
Perfect for applications requiring secure execution of untrusted code in production environments.