不安全的 Bean 验证¶
ID: java/insecure-bean-validation
Kind: path-problem
Security severity: 9.3
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-094
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
约束验证器的自定义错误消息支持不同类型的插值,包括 Java EL 表达式。控制传递给 ConstraintValidatorContext.buildConstraintViolationWithTemplate()
参数的消息模板的一部分会导致任意 Java 代码执行。不幸的是,经过验证(因此通常不受信任)的 bean 属性流入自定义错误消息的情况很常见。
建议¶
有不同的方法来解决此问题
不要在自定义错误消息中包含经过验证的 bean 属性。
使用参数化消息,而不是字符串连接。例如
HibernateConstraintValidatorContext context =
constraintValidatorContext.unwrap(HibernateConstraintValidatorContext.class);
context.addMessageParameter("foo", "bar");
context.buildConstraintViolationWithTemplate("My violation message contains a parameter {foo}")
.addConstraintViolation();
清理经过验证的 Bean 属性以确保没有 EL 表达式。可以在 此处 找到有效清理逻辑的示例。
禁用 EL 插值,仅使用
ParameterMessageInterpolator
Validator validator = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory()
.getValidator();
将 Hibernate Validator 替换为 Apache BVal,其最新版本默认不插值 EL 表达式。请注意,此替换可能不是简单的直接替换。
示例¶
以下验证器可能导致任意 Java 代码执行
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestValidator implements ConstraintValidator<Object, String> {
public static class InterpolationHelper {
public static final char BEGIN_TERM = '{';
public static final char END_TERM = '}';
public static final char EL_DESIGNATOR = '$';
public static final char ESCAPE_CHARACTER = '\\';
private static final Pattern ESCAPE_MESSAGE_PARAMETER_PATTERN = Pattern.compile( "([\\" + ESCAPE_CHARACTER + BEGIN_TERM + END_TERM + EL_DESIGNATOR + "])" );
private InterpolationHelper() {
}
public static String escapeMessageParameter(String messageParameter) {
if ( messageParameter == null ) {
return null;
}
return ESCAPE_MESSAGE_PARAMETER_PATTERN.matcher( messageParameter ).replaceAll( Matcher.quoteReplacement( String.valueOf( ESCAPE_CHARACTER ) ) + "$1" );
}
}
@Override
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
String value = 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 escaped
String escaped = InterpolationHelper.escapeMessageParameter(value);
constraintContext.buildConstraintViolationWithTemplate(escaped).addConstraintViolation().disableDefaultConstraintViolation();
// Good: Bean properties (normally user-controlled) are parameterized
HibernateConstraintValidatorContext context = constraintContext.unwrap( HibernateConstraintValidatorContext.class );
context.addMessageParameter( "prop", object );
context.buildConstraintViolationWithTemplate( "{prop} is invalid").addConstraintViolation();
return false;
}
}
参考¶
Hibernate 参考指南:ConstraintValidatorContext。
GitHub 安全实验室研究:Bean 验证。
常见弱点枚举:CWE-94。