|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2018 the original author or authors. |
| 2 | + * Copyright 2002-2022 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
23 | 23 | import java.util.Collections;
|
24 | 24 | import java.util.HashMap;
|
25 | 25 | import java.util.List;
|
26 |
| -import java.util.Locale; |
27 | 26 | import java.util.Map;
|
28 | 27 | import java.util.Optional;
|
29 | 28 |
|
|
53 | 52 | import org.springframework.util.StringUtils;
|
54 | 53 |
|
55 | 54 | /**
|
56 |
| - * Binder that allows for setting property values onto a target object, |
57 |
| - * including support for validation and binding result analysis. |
58 |
| - * The binding process can be customized through specifying allowed fields, |
| 55 | + * Binder that allows for setting property values on a target object, including |
| 56 | + * support for validation and binding result analysis. |
| 57 | + * |
| 58 | + * <p>The binding process can be customized by specifying allowed field patterns, |
59 | 59 | * required fields, custom editors, etc.
|
60 | 60 | *
|
61 |
| - * <p>Note that there are potential security implications in failing to set an array |
62 |
| - * of allowed fields. In the case of HTTP form POST data for example, malicious clients |
63 |
| - * can attempt to subvert an application by supplying values for fields or properties |
64 |
| - * that do not exist on the form. In some cases this could lead to illegal data being |
65 |
| - * set on command objects <i>or their nested objects</i>. For this reason, it is |
66 |
| - * <b>highly recommended to specify the {@link #setAllowedFields allowedFields} property</b> |
67 |
| - * on the DataBinder. |
| 61 | + * <p><strong>WARNING</strong>: Data binding can lead to security issues by exposing |
| 62 | + * parts of the object graph that are not meant to be accessed or modified by |
| 63 | + * external clients. Therefore, the design and use of data binding should be considered |
| 64 | + * carefully with regard to security. For more details, please refer to the dedicated |
| 65 | + * sections on data binding for |
| 66 | + * <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-initbinder-model-design">Spring Web MVC</a> and |
| 67 | + * <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-initbinder-model-design">Spring WebFlux</a> |
| 68 | + * in the reference manual. |
68 | 69 | *
|
69 | 70 | * <p>The binding results can be examined via the {@link BindingResult} interface,
|
70 | 71 | * extending the {@link Errors} interface: see the {@link #getBindingResult()} method.
|
@@ -174,7 +175,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
174 | 175 | * if the binder is just used to convert a plain parameter value)
|
175 | 176 | * @see #DEFAULT_OBJECT_NAME
|
176 | 177 | */
|
177 |
| - public DataBinder(Object target) { |
| 178 | + public DataBinder(Object target) { |
178 | 179 | this(target, DEFAULT_OBJECT_NAME);
|
179 | 180 | }
|
180 | 181 |
|
@@ -292,7 +293,7 @@ protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
|
292 | 293 | public void initDirectFieldAccess() {
|
293 | 294 | Assert.state(this.bindingResult == null,
|
294 | 295 | "DataBinder is already initialized - call initDirectFieldAccess before other configuration methods");
|
295 |
| - this.bindingResult = createDirectFieldBindingResult(); |
| 296 | + this.directFieldAccess = true; |
296 | 297 | }
|
297 | 298 |
|
298 | 299 | /**
|
@@ -427,13 +428,21 @@ public boolean isIgnoreInvalidFields() {
|
427 | 428 | }
|
428 | 429 |
|
429 | 430 | /**
|
430 |
| - * Register fields that should be allowed for binding. Default is all |
431 |
| - * fields. Restrict this for example to avoid unwanted modifications |
432 |
| - * by malicious users when binding HTTP request parameters. |
433 |
| - * <p>Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching |
434 |
| - * can be implemented by overriding the {@code isAllowed} method. |
435 |
| - * <p>Alternatively, specify a list of <i>disallowed</i> fields. |
436 |
| - * @param allowedFields array of field names |
| 431 | + * Register field patterns that should be allowed for binding. |
| 432 | + * <p>Default is all fields. |
| 433 | + * <p>Restrict this for example to avoid unwanted modifications by malicious |
| 434 | + * users when binding HTTP request parameters. |
| 435 | + * <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and |
| 436 | + * {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as |
| 437 | + * well as direct equality. |
| 438 | + * <p>The default implementation of this method stores allowed field patterns |
| 439 | + * in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical} |
| 440 | + * form. Subclasses which override this method must therefore take this into |
| 441 | + * account. |
| 442 | + * <p>More sophisticated matching can be implemented by overriding the |
| 443 | + * {@link #isAllowed} method. |
| 444 | + * <p>Alternatively, specify a list of <i>disallowed</i> field patterns. |
| 445 | + * @param allowedFields array of allowed field patterns |
437 | 446 | * @see #setDisallowedFields
|
438 | 447 | * @see #isAllowed(String)
|
439 | 448 | * @see org.springframework.web.bind.ServletRequestDataBinder
|
@@ -479,8 +488,7 @@ public void setDisallowedFields(String... disallowedFields) {
|
479 | 488 | else {
|
480 | 489 | String[] fieldPatterns = new String[disallowedFields.length];
|
481 | 490 | for (int i = 0; i < fieldPatterns.length; i++) {
|
482 |
| - String field = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]); |
483 |
| - fieldPatterns[i] = field.toLowerCase(Locale.ROOT); |
| 491 | + fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]); |
484 | 492 | }
|
485 | 493 | this.disallowedFields = fieldPatterns;
|
486 | 494 | }
|
@@ -577,6 +585,7 @@ public void setValidator(Validator validator) {
|
577 | 585 | this.validators.clear();
|
578 | 586 | this.validators.add(validator);
|
579 | 587 | }
|
| 588 | + } |
580 | 589 |
|
581 | 590 | private void assertValidators(Validator... validators) {
|
582 | 591 | Assert.notNull(validators, "Validators required");
|
@@ -813,8 +822,13 @@ protected void checkAllowedFields(MutablePropertyValues mpvs) {
|
813 | 822 | protected boolean isAllowed(String field) {
|
814 | 823 | String[] allowed = getAllowedFields();
|
815 | 824 | String[] disallowed = getDisallowedFields();
|
816 |
| - return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && |
817 |
| - (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase(Locale.ROOT)))); |
| 825 | + if (!ObjectUtils.isEmpty(allowed) && !PatternMatchUtils.simpleMatch(allowed, field)) { |
| 826 | + return false; |
| 827 | + } |
| 828 | + if (!ObjectUtils.isEmpty(disallowed)) { |
| 829 | + return !PatternMatchUtils.simpleMatchIgnoreCase(disallowed, field); |
| 830 | + } |
| 831 | + return true; |
818 | 832 | }
|
819 | 833 |
|
820 | 834 | /**
|
|
0 commit comments