Skip to content

Commit a7cf19c

Browse files
committed
Improve documentation and matching algorithm in data binders
1 parent 0cf7f7b commit a7cf19c

File tree

16 files changed

+402
-203
lines changed

16 files changed

+402
-203
lines changed

spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,8 +23,9 @@
2323

2424
/**
2525
* Common interface for classes that can access named properties
26-
* (such as bean properties of an object or fields in an object)
27-
* Serves as base interface for {@link BeanWrapper}.
26+
* (such as bean properties of an object or fields in an object).
27+
*
28+
* <p>Serves as base interface for {@link BeanWrapper}.
2829
*
2930
* @author Juergen Hoeller
3031
* @since 1.1

spring-context/src/main/java/org/springframework/validation/DataBinder.java

+75-42
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,18 +51,20 @@
5151
import org.springframework.util.StringUtils;
5252

5353
/**
54-
* Binder that allows for setting property values onto a target object,
55-
* including support for validation and binding result analysis.
56-
* The binding process can be customized through specifying allowed fields,
54+
* Binder that allows for setting property values on a target object, including
55+
* support for validation and binding result analysis.
56+
*
57+
* <p>The binding process can be customized by specifying allowed field patterns,
5758
* required fields, custom editors, etc.
5859
*
59-
* <p>Note that there are potential security implications in failing to set an array
60-
* of allowed fields. In the case of HTTP form POST data for example, malicious clients
61-
* can attempt to subvert an application by supplying values for fields or properties
62-
* that do not exist on the form. In some cases this could lead to illegal data being
63-
* set on command objects <i>or their nested objects</i>. For this reason, it is
64-
* <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b>
65-
* on the DataBinder.
60+
* <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing
61+
* parts of the object graph that are not meant to be accessed or modified by
62+
* external clients. Therefore the design and use of data binding should be considered
63+
* carefully with regard to security. For more details, please refer to the dedicated
64+
* sections on data binding for
65+
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and
66+
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a>
67+
* in the reference manual.
6668
*
6769
* <p>The binding results can be examined via the {@link BindingResult} interface,
6870
* extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
@@ -96,6 +98,7 @@
9698
* @author Rob Harrop
9799
* @author Stephane Nicoll
98100
* @author Kazuki Shimizu
101+
* @author Sam Brannen
99102
* @see #setAllowedFields
100103
* @see #setRequiredFields
101104
* @see #registerCustomEditor
@@ -418,15 +421,21 @@ public boolean isIgnoreInvalidFields() {
418421
}
419422

420423
/**
421-
* Register fields that should be allowed for binding. Default is all fields.
422-
* Restrict this for example to avoid unwanted modifications by malicious
424+
* Register field patterns that should be allowed for binding.
425+
* <p>Default is all fields.
426+
* <p>Restrict this for example to avoid unwanted modifications by malicious
423427
* users when binding HTTP request parameters.
424-
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
425-
* arbitrary number of pattern parts), as well as direct equality. More
426-
* sophisticated matching can be implemented by overriding the
427-
* {@code isAllowed} method.
428-
* <p>Alternatively, specify a list of <i>disallowed</i> fields.
429-
* @param allowedFields array of field names
428+
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
429+
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
430+
* well as direct equality.
431+
* <p>The default implementation of this method stores allowed field patterns
432+
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
433+
* form. Subclasses which override this method must therefore take this into
434+
* account.
435+
* <p>More sophisticated matching can be implemented by overriding the
436+
* {@link #isAllowed} method.
437+
* <p>Alternatively, specify a list of <i>disallowed</i> field patterns.
438+
* @param allowedFields array of allowed field patterns
430439
* @see #setDisallowedFields
431440
* @see #isAllowed(String)
432441
*/
@@ -435,34 +444,54 @@ public void setAllowedFields(@Nullable String... allowedFields) {
435444
}
436445

437446
/**
438-
* Return the fields that should be allowed for binding.
439-
* @return array of field names
447+
* Return the field patterns that should be allowed for binding.
448+
* @return array of allowed field patterns
449+
* @see #setAllowedFields(String...)
440450
*/
441451
@Nullable
442452
public String[] getAllowedFields() {
443453
return this.allowedFields;
444454
}
445455

446456
/**
447-
* Register fields that should <i>not</i> be allowed for binding. Default
448-
* is none. Mark fields as disallowed for example to avoid unwanted
457+
* Register field patterns that should <i>not</i> be allowed for binding.
458+
* <p>Default is none.
459+
* <p>Mark fields as disallowed, for example to avoid unwanted
449460
* modifications by malicious users when binding HTTP request parameters.
450-
* <p>Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
451-
* arbitrary number of pattern parts), as well as direct equality.
452-
* More sophisticated matching can be implemented by overriding the
453-
* {@code isAllowed} method.
454-
* <p>Alternatively, specify a list of <i>allowed</i> fields.
455-
* @param disallowedFields array of field names
461+
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
462+
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
463+
* well as direct equality.
464+
* <p>The default implementation of this method stores disallowed field patterns
465+
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
466+
* form. As of Spring Framework 5.2.21, the default implementation also transforms
467+
* disallowed field patterns to {@linkplain String#toLowerCase() lowercase} to
468+
* support case-insensitive pattern matching in {@link #isAllowed}. Subclasses
469+
* which override this method must therefore take both of these transformations
470+
* into account.
471+
* <p>More sophisticated matching can be implemented by overriding the
472+
* {@link #isAllowed} method.
473+
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
474+
* @param disallowedFields array of disallowed field patterns
456475
* @see #setAllowedFields
457476
* @see #isAllowed(String)
458477
*/
459478
public void setDisallowedFields(@Nullable String... disallowedFields) {
460-
this.disallowedFields = PropertyAccessorUtils.canonicalPropertyNames(disallowedFields);
479+
if (disallowedFields == null) {
480+
this.disallowedFields = null;
481+
}
482+
else {
483+
String[] fieldPatterns = new String[disallowedFields.length];
484+
for (int i = 0; i < fieldPatterns.length; i++) {
485+
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]).toLowerCase();
486+
}
487+
this.disallowedFields = fieldPatterns;
488+
}
461489
}
462490

463491
/**
464-
* Return the fields that should <i>not</i> be allowed for binding.
465-
* @return array of field names
492+
* Return the field patterns that should <i>not</i> be allowed for binding.
493+
* @return array of disallowed field patterns
494+
* @see #setDisallowedFields(String...)
466495
*/
467496
@Nullable
468497
public String[] getDisallowedFields() {
@@ -774,16 +803,20 @@ protected void checkAllowedFields(MutablePropertyValues mpvs) {
774803
}
775804

776805
/**
777-
* Return if the given field is allowed for binding.
778-
* Invoked for each passed-in property value.
779-
* <p>The default implementation checks for "xxx*", "*xxx", "*xxx*" and "xxx*yyy"
780-
* matches (with an arbitrary number of pattern parts), as well as direct equality,
781-
* in the specified lists of allowed fields and disallowed fields. A field matching
782-
* a disallowed pattern will not be accepted even if it also happens to match a
783-
* pattern in the allowed list.
784-
* <p>Can be overridden in subclasses.
806+
* Determine if the given field is allowed for binding.
807+
* <p>Invoked for each passed-in property value.
808+
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
809+
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
810+
* well as direct equality, in the configured lists of allowed field patterns
811+
* and disallowed field patterns.
812+
* <p>Matching against allowed field patterns is case-sensitive; whereas,
813+
* matching against disallowed field patterns is case-insensitive.
814+
* <p>A field matching a disallowed pattern will not be accepted even if it
815+
* also happens to match a pattern in the allowed list.
816+
* <p>Can be overridden in subclasses, but care must be taken to honor the
817+
* aforementioned contract.
785818
* @param field the field to check
786-
* @return if the field is allowed
819+
* @return {@code true} if the field is allowed
787820
* @see #setAllowedFields
788821
* @see #setDisallowedFields
789822
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
@@ -792,7 +825,7 @@ protected boolean isAllowed(String field) {
792825
String[] allowed = getAllowedFields();
793826
String[] disallowed = getDisallowedFields();
794827
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
795-
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field)));
828+
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase())));
796829
}
797830

798831
/**

0 commit comments

Comments
 (0)