설명
Apache Commons JEXL은 문자열을 표현식이나 스크립트로 파싱하고 나중에 컨텍스트 데이터와 함께 실행합니다. JXLT는 JEXL을 사용하는 템플릿 엔진입니다. 신뢰할 수 없는 텍스트가 이들의 소스 인자에 도달하면 공격자가 애플리케이션이 실행할 연산과 제어 흐름을 작성할 수 있습니다.
영향은 JEXL 버전, 엔진 기능, 권한, 네임스페이스, 컨텍스트에 노출한 객체 및 실제 실행 여부에 따라 달라집니다. 공격자는 업무 조건을 변경하거나 노출된 객체의 데이터와 메서드에 접근하고 자원을 과도하게 사용할 수 있습니다. 광범위한 권한이나 레거시 설정에서는 파일, 네트워크, 리플렉션, 클래스 로딩 또는 프로세스 실행까지 가능할 수 있습니다.
JEXL 3.7.0은 기본 권한을 SECURE로 변경하고 new(...), 전역 부작용, pragma, annotation 등을 기본 기능에서 비활성화했습니다. 그러나 공식 문서는 SECURE와 RESTRICTED 모두 신뢰할 수 없는 사용자 입력을 실행하기 위한 완전한 샌드박스가 아니라고 명시합니다. 스크립트의 반복문은 기본 기능에서도 허용됩니다.
잠재적 영향
- 평가 결과를 사용하는 인증, 가격, 필터 또는 업무 조건 변경
- 컨텍스트, 네임스페이스 또는 허용된 클래스가 노출하는 데이터 조회와 상태 변경
- 권한이 넓은 엔진에서 파일·네트워크·리플렉션·클래스 로딩·프로세스 API 접근
- 긴 소스, 반복문, 큰 범위 또는 과도한 계산에 의한 CPU·메모리·출력 고갈
해결 방법
- 표현식, 스크립트 및 템플릿 소스는 개발자가 관리하는 고정 문자열로 유지합니다. 요청 값은
JexlContext데이터나 선언된 스크립트 인자로만 전달하고 소스 문자열에 연결하지 않습니다. - 사용자가 동작을 선택해야 한다면 유한한 서버 소유 키를 검토된 상수 프로그램이나 Java 함수에 매핑합니다. 단순 계산만 필요하면 더 작은 문법을 가진 전용 파서를 우선 사용합니다.
- 사용자 작성 JEXL이 제품 요구 사항이라면 지원되는 최신 릴리스를 사용하고
JexlPermissions.NONE또는JexlPermissions.create(...)에서 시작해 필요한 클래스와 멤버만 양의 규칙으로 허용합니다.UNRESTRICTED를 사용하거나 레거시 기본 설정을 복원하지 않으며,SECURE또는RESTRICTED만으로 충분하다고 가정하지 않습니다. JexlFeatures.createNone()에서 시작해 필요한 문법만 활성화합니다. 컨텍스트 객체, 네임스페이스, import, 산술 확장 및 클래스 로더 접근을 최소화합니다.JexlBuilder.uberspect(...)에 커스텀 구현을 전달하면 빌더 권한 동작을 대체하므로 별도로 검토해야 합니다.- 소스 길이와 복잡도를 제한하고 실행 시간, CPU, 메모리 및 출력량을 강제 제한합니다.
cancellable(true)를 구성하고 제한 시간에 실행 스레드를 중단하되, 강한 격리가 필요하면 애플리케이션 밖의 별도 프로세스나 컨테이너에서 실행합니다. - 정규식이나 토큰 차단 목록은 임의의 JEXL 소스를 안전하게 만드는 새니타이저가 아닙니다. 정책은 허용할 문법과 객체 기능을 닫힌 집합으로 정의하고 실제 허용 결정을 테스트해야 합니다.
예시
변경 전
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
class UnsafeJexlController {
@PostMapping("/execute")
Object execute(@RequestParam("script") String script) {
JexlEngine engine = new JexlBuilder().create();
JexlScript compiled = engine.createScript(script); // BAD: 요청을 코드로 파싱
return compiled.execute(new MapContext());
}
}
JEXL 3.7의 기본 설정은 이전 버전보다 엄격하지만, 요청 전체가 여전히 프로그램으로 해석됩니다. 특히 스크립트에는 반복문이 포함될 수 있고, 애플리케이션이 추가한 권한과 컨텍스트 객체가 공격 표면을 넓힙니다.
변경 후
소스를 고정하고 외부 값은 컨텍스트 데이터로 분리합니다.
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
class SafeJexlController {
private final JexlEngine engine = new JexlBuilder().create();
@GetMapping("/total")
double total(@RequestParam("price") double price,
@RequestParam("quantity") int quantity) {
MapContext context = new MapContext();
context.set("price", price);
context.set("quantity", quantity);
JexlExpression fixed = engine.createExpression("price * quantity");
return ((Number) fixed.evaluate(context)).doubleValue();
}
}
사용자 작성 문법이 꼭 필요한 경우 다음과 같이 닫힌 권한·기능 집합에서 시작할 수 있지만, 이는 입력 제한과 자원 격리를 대체하지 않습니다.
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlFeatures;
import org.apache.commons.jexl3.introspection.JexlPermissions;
JexlEngine constrained = new JexlBuilder()
.permissions(JexlPermissions.create()) // 기본적으로 모든 리플렉션 접근 거부
.features(JexlFeatures.createNone()) // 필요한 문법만 명시적으로 추가
.cancellable(true)
.strict(true)
.silent(false)
.create();
참조
- Apache Commons JEXL 3.7.0
- JEXL 3.7.0 release notes
- JEXL package guide: configuration and security
- JexlEngine API
- JxltEngine API
- JexlPermissions API and security disclaimer
- JexlFeatures API
- JexlSandbox API
- Legacy JEXL 2.1 JexlEngine API
- CWE-94: Improper Control of Generation of Code
- OWASP Top 10:2025 A05 Injection
- OWASP Top 10:2021 A03 Injection
- OWASP ASVS 5.0