-
Notifications
You must be signed in to change notification settings - Fork 182
SecurityGuard
Any interpreted language has the common issue of running unknown source code, which can be malicious. For example, suppose you are using BeanShell to create an AWS Lambda competitor, where you let your users create Java code and run it, how do you know that the user’s code isn’t malicious? How do you know it won’t cause any harmful effects, like stopping the entire server or dumping server files, including the application’s source code? To solve that we have SecurityGuard, the API to prevent the execution of malicious code.
In code, SecurityGuard is just an interface to be implemented and added into bsh.Interpreter
Note: The example below is just a basic example; it doesn’t prevent the execution of anything!
import bsh.Interpreter;
import bsh.security.SecurityGuard;
public class Main {
public static void main(String[] args) throws Throwable {
// This is your implementation of SecurityGuard
class MySecurityGuard implements SecurityGuard {}
// Add your SecurityGuard to be used by the Interpreter
Interpreter.mainSecurityGuard.add(new MySecurityGuard());
// Create an interpreter instance an evaluate the code
Interpreter interpreter = new Interpreter();
interpreter.eval("... code to be evaluated");
}
}In the given example above, MySecurityGuard has no validation, thus it don't prevent the execution of anything, but how can we do that ?
As mentioned in the beginning, SecurityGuard is just an interface to be implemented, all methods of this interface are used to validate the execution of some specific thing and you can implement each method to have your own logic to prevent the execution of something that you want to. Here are a few examples of what’s possible:
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canConstruct(Class<?> _class, Object[] args) {
if (List.class.isAssignableFrom(_class))
return false; // Note: can't create the object if it's going to be a List
return true;
}
}BeanShell code:
import java.util.*;
var myMap = new HashMap<Object, Object>(); // No problem creating a HashMap
var myList = new ArrayList<Object>(); // SecurityError: Can't call this construct: new java.util.List();
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canInvokeMethod(Object thisArg, String methodName, Object[] args) {
if (thisArg instanceof List && methodName.equals("size"))
return false; // Note: can't call the method '.size()' of a List
return false;
}
}BeanShell code:
import java.util.*;
var myList = new ArrayList<String>();
myList.add("Hello");
myList.add("World");
myList.size(); // SecurityError: Can't invoke this method: java.util.ArrayList.size();
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canInvokeStaticMethod(Class<?> _class, String methodName, Object[] args) {
if (_class == Collections.class && methodName.equals("emptyList"))
return false; // Note: can't call the method '.emptyList()' of a Collections
return true;
}
}BeanShell code:
import java.util.*;
Collections.emptyMap();
Collections.emptyList(); // SecurityError: Can't invoke this static method: java.util.Collections.emptyList();
SecurityGuard:
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canInvokeLocalMethod(String methodName, Object[] args) {
if (methodName.equals("eval"))
return false; // Note: can't call the local method 'eval()'
return true;
}
}BeanShell code:
clear();
eval("30 * 3"); // SecurityError: Can't invoke this local method: eval(java.lang.String)
SecurityGuard:
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canGetField(Object thisArg, String fieldName) {
if (thisArg.getClass().isArray() && fieldName.equals("length"))
return false; // Note: prevent getting the field 'length' of any array
return true;
}
}BeanShell code:
class Cls1 {
int myNum = 82;
}
new Cls1().myNum;
new Object[0].length; // SecurityError: Can't get this field: java.lang.Object[].length
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canGetStaticField(Class<?> _class, String fieldName) {
if (Collections.class == _class && fieldName.equals("EMPTY_MAP"))
return false; // Note: prevent getting the static field 'EMPTY_MAP' of Collections
return true;
}
}BeanShell code:
import java.util.*;
Collections.EMPTY_LIST;
Collections.EMPTY_MAP; // SecurityError: Can't get this static field: java.util.Collections.EMPTY_MAP
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canExtends(Class<?> superClass) {
if (superClass == HashMap.class)
return false; // Note: prevent extending the class HashMap
return true;
}
}BeanShell code:
import java.util.*;
class MyList<T> extends ArrayList<T> {}
class MyMap<K, V> extends HashMap<K, V> {} // SecurityError: Can't extend this class: java.util.HashMap
SecurityGuard:
import java.util.*;
import bsh.security.SecurityGuard;
class MySecurityGuard implements SecurityGuard {
@Override
public boolean canImplements(Class<?> _interface) {
if (_interface == List.class)
return false; // Note: prevent implementing the interface List
return true;
}
}BeanShell code:
import java.util.*;
class MyMap<K, V> extends HashMap<K, V> implements Map<K, V> {}
class MyList<T> extends ArrayList<T> implements List<T> {} // Can't implement this interface: java.util.List