How One Line of Code Opened a Remote Code Execution Hole in SpringBoot
A SpringBoot project’s custom validator introduced a severe remote code execution vulnerability when a single line of code interpolated user input, illustrating the importance of rigorous input validation, internationalized error handling, and security scanning before deployment.
Background
In a global‑user SpringBoot web project we built a unified exception handling component that centralises error codes and messages, using internationalised resource files to return locale‑specific messages.
Internationalised Error Example
When a verification code expires we throw throw new ErrorCodeException(ErrorCodes.AUTHCODE_EXPIRED). The message is then looked up in language files, e.g.:
Chinese: "您输入的验证码已过期,请重新获取"
English: "The verification code you input is expired, ..."
German: "Der von Ihnen eingegebene Verifizierungscode ist abgelaufen, bitte wiederholen"
The core of this mechanism is a GlobalExceptionHandler that intercepts all controller exceptions and retrieves the appropriate i18n message.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BadRequestException.class)
@ResponseBody
public ResponseEntity handle(HttpServletRequest request, BadRequestException e) {
String i18message = getI18nMessage(e.getKey(), request);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.error(e.getCode(), i18message));
}
@ExceptionHandler(ErrorCodeException.class)
@ResponseBody
public ResponseEntity handle(HttpServletRequest request, ErrorCodeException e) {
String i18message = getI18nMessage(e.getKey(), request);
return ResponseEntity.status(HttpStatus.OK).body(Response.error(e.getCode(), i18message));
}
}Form Validation with Custom Annotations
We defined a UserRegForm class and added constraints:
public class UserRegForm {
@Length(min = 6, max = 20, message = "validate.userRegForm.nickname")
private String nickname;
@Gender(message = "validate.userRegForm.gender")
private String gender;
@NotNull
@Email(message = "validate.userRegForm.email")
private String email;
}The custom @Gender annotation is backed by a validator that checks the value against an allowed list.
@Documented
@Constraint(validatedBy = CustomValidator.class)
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomParam {
String message() default "validate.custom.default";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@interface List { CustomParam[] value(); }
} public class CustomValidator implements ConstraintValidator<CustomParam, String> {
@Override
public boolean isValid(String s, ConstraintValidatorContext ctx) {
if (s == null || s.isEmpty()) return true;
if (s.equals("tanglei")) return true;
error(ctx, "Invalid params: " + s);
return false;
}
private static void error(ConstraintValidatorContext ctx, String msg) {
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate(msg).addConstraintViolation();
}
}Security Incident
Before release the security team scanned the code and discovered that the custom validator’s error message was built by concatenating the raw user input:
error(ctx, "Invalid params: " + s);This allowed an attacker to inject a Spring Expression Language (SpEL) expression such as 1+1=${1+1}. The expression was interpolated by Hibernate Validator’s message interpolator, leading to evaluation of arbitrary EL code and ultimately remote code execution (e.g., executing ls -al or ping www.tanglei.name).
The root cause was the unchecked interpolation of user‑supplied strings in the validation error message, which the validator treated as a message template and evaluated.
Lessons Learned
Never trust user input; always validate and sanitize before using it in message templates or code.
Apply the principle of least privilege to the runtime user of your services.
Use automated security scanning tools to catch such vulnerabilities early.
Keep internationalised messages static or use safe placeholders instead of raw user data.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
