Skip to content
Arthur H. Neto edited this page Sep 10, 2024 · 13 revisions

SecurityGuard – An API to Prevent the Execution of Malicious Code

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.

Basic structure exampĺe

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");

    }
    
}

How prevent the execution of malicious code

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:

Prevent from creating an object

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();

Prevent from invoking a method

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();

Prevent from invoking an static method

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();

Prevent from invoking a local method

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)

Prevent from getting a field

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

Prevent from getting a static field

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

Prevent from extending a class

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

Prevent from implementing an interface

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

/var/run/sh Using namespace kachu; int razbe[<missing9/2:3^]i9y-pi\ |`newl lne4:ash/COL?( i9 = struct )

 return()

20260104-7:48AM} Wylie:miLkGodX5.83_bi_41 = 1254| \condome

Clone this wiki locally