Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit debfc68

Browse files
Alvaro Muñozpwntester
authored andcommitted
Insecure Bean Validation query
1 parent 7d7933a commit debfc68

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import javax.validation.ConstraintValidator;
2+
import javax.validation.ConstraintValidatorContext;
3+
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
4+
import java.util.regex.Matcher;
5+
import java.util.regex.Pattern;
6+
7+
public class TestValidator implements ConstraintValidator<Object, String> {
8+
9+
public static class InterpolationHelper {
10+
11+
public static final char BEGIN_TERM = '{';
12+
public static final char END_TERM = '}';
13+
public static final char EL_DESIGNATOR = '$';
14+
public static final char ESCAPE_CHARACTER = '\\';
15+
16+
private static final Pattern ESCAPE_MESSAGE_PARAMETER_PATTERN = Pattern.compile( "([\\" + ESCAPE_CHARACTER + BEGIN_TERM + END_TERM + EL_DESIGNATOR + "])" );
17+
18+
private InterpolationHelper() {
19+
}
20+
21+
public static String escapeMessageParameter(String messageParameter) {
22+
if ( messageParameter == null ) {
23+
return null;
24+
}
25+
return ESCAPE_MESSAGE_PARAMETER_PATTERN.matcher( messageParameter ).replaceAll( Matcher.quoteReplacement( String.valueOf( ESCAPE_CHARACTER ) ) + "$1" );
26+
}
27+
28+
}
29+
30+
@Override
31+
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
32+
String value = object + " is invalid";
33+
34+
// Bad: Bean properties (normally user-controlled) are passed directly to `buildConstraintViolationWithTemplate`
35+
constraintContext.buildConstraintViolationWithTemplate(value).addConstraintViolation().disableDefaultConstraintViolation();
36+
37+
// Good: Bean properties (normally user-controlled) are escaped
38+
String escaped = InterpolationHelper.escapeMessageParameter(value);
39+
constraintContext.buildConstraintViolationWithTemplate(escaped).addConstraintViolation().disableDefaultConstraintViolation();
40+
41+
// Good: Bean properties (normally user-controlled) are parameterized
42+
HibernateConstraintValidatorContext context = constraintContext.unwrap( HibernateConstraintValidatorContext.class );
43+
context.addMessageParameter( "prop", object );
44+
context.buildConstraintViolationWithTemplate( "{prop} is invalid").addConstraintViolation();
45+
return false;
46+
}
47+
48+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>When building custom constraint violation error messages, it is important to understand that they support different types of interpolation, including [Java EL expressions](https://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-message-interpolation.html#section-interpolation-with-message-expressions). Therefore if an attacker can inject arbitrary data in the error message template being passed to `ConstraintValidatorContext.buildConstraintViolationWithTemplate()` argument, he will be able to run arbitrary Java code. Unfortunately, it is common that validated (and therefore, normally untrusted) bean properties flow into the custom error message.</p>
8+
</overview>
9+
10+
<recommendation>
11+
<p>There are different approaches to remediate the issue:</p>
12+
<li>Do not include validated bean properties in the custom error message.</li>
13+
<li>Use parameterized messages instead of string concatenation. E.g:</li>
14+
``` java
15+
HibernateConstraintValidatorContext context = constraintValidatorContext.unwrap( HibernateConstraintValidatorContext.class );
16+
context.addMessageParameter( "foo", "bar" );
17+
context.buildConstraintViolationWithTemplate( "My violation message contains a parameter {foo}").addConstraintViolation();
18+
```
19+
<li>Sanitize the validated bean properties to make sure that there are no EL expressions. An example of valid sanitization logic can be found [here](https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/engine/messageinterpolation/util/InterpolationHelper.java#L17).
20+
- Disable the EL interpolation and only use `ParameterMessageInterpolator`:</li>
21+
``` java
22+
Validator validator = Validation.byDefaultProvider()
23+
.configure()
24+
.messageInterpolator( new ParameterMessageInterpolator() )
25+
.buildValidatorFactory()
26+
.getValidator();
27+
```
28+
<li>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.</li>
29+
</recommendation>
30+
31+
<example>
32+
<p>The following Validator could result in arbitrary Java code execution:</p>
33+
<sample src="InsecureBeanValidation.java" />
34+
</example>
35+
36+
<references>
37+
<li>https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#_the_code_constraintvalidatorcontext_code</li>
38+
</references>
39+
</qhelp>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @name Insecure Bean validation
3+
* @description User-controlled data may be evaluated as a Java EL expressions, leading to arbitrary code execution.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @precision high
7+
* @id java/unsafe-eval
8+
* @tags security
9+
* external/cwe/cwe-094
10+
*/
11+
12+
import java
13+
import semmle.code.java.dataflow.TaintTracking
14+
import semmle.code.java.dataflow.FlowSources
15+
import DataFlow::PathGraph
16+
17+
class BeanValidationSource extends RemoteFlowSource {
18+
BeanValidationSource() {
19+
exists(Method m, Parameter v |
20+
this.asParameter() = v and
21+
m.getParameter(0) = v and
22+
m
23+
.getDeclaringType()
24+
.getASupertype+()
25+
.getSourceDeclaration()
26+
.hasQualifiedName("javax.validation", "ConstraintValidator") and
27+
m.hasName("isValid") and
28+
m.fromSource()
29+
)
30+
}
31+
32+
override string getSourceType() { result = "BeanValidation source" }
33+
}
34+
35+
class ExceptionTaintStep extends TaintTracking::AdditionalTaintStep {
36+
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
37+
exists(Call call, TryStmt t, CatchClause c, MethodAccess gm |
38+
call.getEnclosingStmt().getEnclosingStmt*() = t.getBlock() and
39+
t.getACatchClause() = c and
40+
(
41+
call.getCallee().getAThrownExceptionType().getASubtype*() = c.getACaughtType() or
42+
c.getACaughtType().getASupertype*() instanceof TypeRuntimeException
43+
) and
44+
c.getVariable().getAnAccess() = gm.getQualifier() and
45+
gm.getMethod().getName().regexpMatch("get(Localized)?Message|toString") and
46+
n1.asExpr() = call.getAnArgument() and
47+
n2.asExpr() = gm
48+
)
49+
}
50+
}
51+
52+
class BuildConstraintViolationWithTemplateMethod extends Method {
53+
BuildConstraintViolationWithTemplateMethod() {
54+
getDeclaringType()
55+
.getASupertype*()
56+
.hasQualifiedName("javax.validation", "ConstraintValidatorContext") and
57+
hasName("buildConstraintViolationWithTemplate")
58+
}
59+
}
60+
61+
class BeanValidationConfig extends TaintTracking::Configuration {
62+
BeanValidationConfig() { this = "BeanValidationConfig" }
63+
64+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
65+
66+
override predicate isSink(DataFlow::Node sink) {
67+
exists(MethodAccess ma |
68+
ma.getMethod() instanceof BuildConstraintViolationWithTemplateMethod and
69+
sink.asExpr() = ma.getArgument(0)
70+
)
71+
}
72+
}
73+
74+
from BeanValidationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
75+
where cfg.hasFlowPath(source, sink)
76+
select sink, source, sink, "Custom constraint error message contains unsanitized user data"

0 commit comments

Comments
 (0)