Insecure functionality
Need
Prevention of expression language injection by never evaluating user input with full SpEL
Context
• Usage of Kotlin 1.9+ on the JVM for building web applications
• Usage of Spring Web MVC and the Spring Expression Language (SpEL)
Description
1. Non compliant code
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class ReportController {
private val parser = SpelExpressionParser()...The `evaluate` endpoint below parses the `expr` request parameter with `SpelExpressionParser` and evaluates it with the default context. `Expression.getValue()` without a context uses a `StandardEvaluationContext`, which gives the expression the full power of SpEL: type references, constructors and method calls on any class of the classpath. A request with `expr=T(java.lang.Runtime).getRuntime().exec('touch /tmp/pwned')` runs an operating system command, and other expressions read environment variables, files or Spring beans. This is remote code execution with the privileges of the application. The same class of problem appears when request data selects classes with `Class.forName` or `ClassLoader.loadClass` and the application instantiates them.
2. Steps
• Never pass request data to `SpelExpressionParser.parseExpression` evaluated with a `StandardEvaluationContext` or with the default `getValue()`.
• When users must select values, validate the input against a strict property-path pattern and evaluate it with `SimpleEvaluationContext.forReadOnlyDataBinding()`.
• Prefer mapping allowed names to Kotlin property accesses over evaluating expressions.
• Never load or instantiate classes whose names come from requests with `Class.forName` or `ClassLoader.loadClass`; select implementations from an explicit registry.
• Keep Spring and the JDK up to date, since expression language sandboxes are regularly hardened.
3. Secure code example
import org.springframework.expression.spel.standard.SpelExpressionParser
import org.springframework.expression.spel.support.SimpleEvaluationContext
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
private val PROPERTY_PATH = Regex("^[A-Za-z]{1,32}(\\.[A-Za-z]{1,32}){0,3}$")
...The corrected endpoint no longer evaluates arbitrary expressions. The caller can only ask for a property of the `Report` object, and the path must match `PROPERTY_PATH`, a strict pattern of dotted names such as `summary.total`, so method calls, type references and operators are rejected before parsing. The expression is evaluated with `SimpleEvaluationContext.forReadOnlyDataBinding()`, which only allows reading properties of the root object. Even if a dangerous construct reached the parser, this context has no type locator, constructor resolver or method resolver, so `T(...)`, `new` and method invocations fail. When the feature only needs a handful of values, mapping each allowed name to a Kotlin property access is simpler still and removes SpEL entirely.
References
• 014. Insecure functionality