Insecure Bean Validation¶
ID: java/insecure-bean-validationKind: 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
Custom error messages for constraint validators support different types of interpolation, includingJava EL expressions. Controlling part of the message template being passed toConstraintValidatorContext.buildConstraintViolationWithTemplate() argument can lead to arbitrary Java code execution. Unfortunately, it is common that validated (and therefore, normally untrusted) bean properties flow into the custom error message.
Recommendation¶
There are different approaches to remediate the issue:
Do not include validated bean properties in the custom error message.
Use parameterized messages instead of string concatenation. For example:
HibernateConstraintValidatorContext context = constraintValidatorContext.unwrap(HibernateConstraintValidatorContext.class);context.addMessageParameter("foo", "bar");context.buildConstraintViolationWithTemplate("My violation message contains a parameter {foo}") .addConstraintViolation();Sanitize the validated bean properties to make sure that there are no EL expressions. An example of valid sanitization logic can be foundhere.
Disable the EL interpolation and only use
ParameterMessageInterpolator:
Validator validator = Validation.byDefaultProvider() .configure() .messageInterpolator(new ParameterMessageInterpolator()) .buildValidatorFactory() .getValidator();
Replace Hibernate Validator with Apache BVal, which in its latest version does not interpolate EL expressions by default. Note that this replacement may not be a simple drop-in replacement.
Example¶
The following validator could result in arbitrary Java code execution:
importjavax.validation.ConstraintValidator;importjavax.validation.ConstraintValidatorContext;importorg.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassTestValidatorimplementsConstraintValidator<Object,String>{publicstaticclassInterpolationHelper{publicstaticfinalcharBEGIN_TERM='{';publicstaticfinalcharEND_TERM='}';publicstaticfinalcharEL_DESIGNATOR='$';publicstaticfinalcharESCAPE_CHARACTER='\\';privatestaticfinalPatternESCAPE_MESSAGE_PARAMETER_PATTERN=Pattern.compile("([\\"+ESCAPE_CHARACTER+BEGIN_TERM+END_TERM+EL_DESIGNATOR+"])");privateInterpolationHelper(){}publicstaticStringescapeMessageParameter(StringmessageParameter){if(messageParameter==null){returnnull;}returnESCAPE_MESSAGE_PARAMETER_PATTERN.matcher(messageParameter).replaceAll(Matcher.quoteReplacement(String.valueOf(ESCAPE_CHARACTER))+"$1");}}@OverridepublicbooleanisValid(Stringobject,ConstraintValidatorContextconstraintContext){Stringvalue=object+" is invalid";// Bad: Bean properties (normally user-controlled) are passed directly to `buildConstraintViolationWithTemplate`constraintContext.buildConstraintViolationWithTemplate(value).addConstraintViolation().disableDefaultConstraintViolation();// Good: Bean properties (normally user-controlled) are escapedStringescaped=InterpolationHelper.escapeMessageParameter(value);constraintContext.buildConstraintViolationWithTemplate(escaped).addConstraintViolation().disableDefaultConstraintViolation();// Good: Bean properties (normally user-controlled) are parameterizedHibernateConstraintValidatorContextcontext=constraintContext.unwrap(HibernateConstraintValidatorContext.class);context.addMessageParameter("prop",object);context.buildConstraintViolationWithTemplate("{prop} is invalid").addConstraintViolation();returnfalse;}}
References¶
Hibernate Reference Guide:ConstraintValidatorContext.
GitHub Security Lab research:Bean validation.
Common Weakness Enumeration:CWE-94.