Expression language injection (JEXL)¶
ID: java/jexl-expression-injectionKind: path-problemSecurity severity: 9.3Severity: errorPrecision: highTags: - security - external/cwe/cwe-094Query suites: - java-code-scanning.qls - java-security-extended.qls - java-security-and-quality.qls
Click to see the query in the CodeQL repository
Java EXpression Language (JEXL) is a simple expression language provided by the Apache Commons JEXL library. The syntax is close to a mix of ECMAScript and shell-script. The language allows invocation of methods available in the JVM. If a JEXL expression is built using attacker-controlled data, and then evaluated, then it may allow the attacker to run arbitrary code.
Recommendation¶
It is generally recommended to avoid using untrusted input in a JEXL expression. If it is not possible, JEXL expressions should be run in a sandbox that allows accessing only explicitly allowed classes.
Example¶
The following example uses untrusted data to build and run a JEXL expression.
publicvoidevaluate(Socketsocket)throwsIOException{try(BufferedReaderreader=newBufferedReader(newInputStreamReader(socket.getInputStream()))){Stringinput=reader.readLine();JexlEnginejexl=newJexlBuilder().create();// BAD: input is controlled by the userJexlExpressionexpression=jexl.createExpression(input);JexlContextcontext=newMapContext();expression.evaluate(context);}}
The next example shows how an untrusted JEXL expression can be run in a sandbox that allows accessing only methods in thejava.lang.Math class. The sandbox is implemented usingJexlSandbox class that is provided by Apache Commons JEXL 3.
publicvoidevaluate(Socketsocket)throwsIOException{try(BufferedReaderreader=newBufferedReader(newInputStreamReader(socket.getInputStream()))){JexlSandboxonlyMath=newJexlSandbox(false);onlyMath.white("java.lang.Math");JexlEnginejexl=newJexlBuilder().sandbox(onlyMath).create();// GOOD: using a sandboxStringinput=reader.readLine();JexlExpressionexpression=jexl.createExpression(input);JexlContextcontext=newMapContext();expression.evaluate(context);}}
The next example shows another way how a sandbox can be implemented. It uses a custom implementation ofJexlUberspect that checks if callees are instances of allowed classes.
publicvoidevaluate(Socketsocket)throwsIOException{try(BufferedReaderreader=newBufferedReader(newInputStreamReader(socket.getInputStream()))){JexlUberspectsandbox=newJexlUberspectSandbox();JexlEnginejexl=newJexlBuilder().uberspect(sandbox).create();Stringinput=reader.readLine();JexlExpressionexpression=jexl.createExpression(input);// GOOD: jexl uses a sandboxJexlContextcontext=newMapContext();expression.evaluate(context);}privatestaticclassJexlUberspectSandboximplementsJexlUberspect{privatestaticfinalList<String>ALLOWED_CLASSES=Arrays.asList("java.lang.Math","java.util.Random");privatefinalJexlUberspectuberspect=newJexlBuilder().create().getUberspect();privatevoidcheckAccess(Objectobj){if(!ALLOWED_CLASSES.contains(obj.getClass().getCanonicalName())){thrownewAccessControlException("Not allowed");}}@OverridepublicJexlMethodgetMethod(Objectobj,Stringmethod,Object...args){checkAccess(obj);returnuberspect.getMethod(obj,method,args);}@OverridepublicList<PropertyResolver>getResolvers(JexlOperatorop,Objectobj){checkAccess(obj);returnuberspect.getResolvers(op,obj);}@OverridepublicvoidsetClassLoader(ClassLoaderloader){uberspect.setClassLoader(loader);}@OverridepublicintgetVersion(){returnuberspect.getVersion();}@OverridepublicJexlMethodgetConstructor(Objectobj,Object...args){checkAccess(obj);returnuberspect.getConstructor(obj,args);}@OverridepublicJexlPropertyGetgetPropertyGet(Objectobj,Objectidentifier){checkAccess(obj);returnuberspect.getPropertyGet(obj,identifier);}@OverridepublicJexlPropertyGetgetPropertyGet(List<PropertyResolver>resolvers,Objectobj,Objectidentifier){checkAccess(obj);returnuberspect.getPropertyGet(resolvers,obj,identifier);}@OverridepublicJexlPropertySetgetPropertySet(Objectobj,Objectidentifier,Objectarg){checkAccess(obj);returnuberspect.getPropertySet(obj,identifier,arg);}@OverridepublicJexlPropertySetgetPropertySet(List<PropertyResolver>resolvers,Objectobj,Objectidentifier,Objectarg){checkAccess(obj);returnuberspect.getPropertySet(resolvers,obj,identifier,arg);}@OverridepublicIterator<?>getIterator(Objectobj){checkAccess(obj);returnuberspect.getIterator(obj);}@OverridepublicJexlArithmetic.UberspectgetArithmetic(JexlArithmeticarithmetic){returnuberspect.getArithmetic(arithmetic);}}}
References¶
Apache Commons JEXL:Project page.
Apache Commons JEXL documentation:JEXL 2.1.1 API.
Apache Commons JEXL documentation:JEXL 3.1 API.
Common Weakness Enumeration:CWE-94.