Abstract
用于将 HTTP 请求参数绑定到模型类的框架绑定器未显式配置为允许或禁止特定属性
Explanation
为便于开发和提高生产率,现代框架允许自动实例化一个对象,并使用名称与要绑定的类的属性相匹配的
HTTP 请求参数填充该对象。对象的自动实例化和填充加快了开发速度,但如果不谨慎实施,会导致严重的
问题。绑定类或嵌套类中的任何属性都将自动绑定到 HTTP 请求参数。因此,恶意用户能够将值分配给绑定
类或嵌套类中的任意属性,即使这些属性未通过 Web 表单或 API 合约暴露给客户端也是如此。 例 1: 只需使
用 Spring MVC 而无需进行额外配置,以下控制器方法即可将 HTTP 请求参数绑定到 User 或 Details 类中
的任何属性:
@RequestMapping(method = RequestMethod.POST)
public String registerUser(@ModelAttribute(“user”) User user, BindingResult
result, SessionStatus status) {
if (db.save(user).hasErrors()) {
return “CustomerForm”;
} else {
status.setComplete();
return “CustomerSuccess”;
}
}
其中, User 类定义为:
public class User {
private String name;
private String lastname;
private int age;
private Details details;
// Public Getters and Setters
…
}
Details 类定义为:
public class Details {
private boolean is_admin;
private int id;
private Date login_date;
// Public Getters and Setters
…
}
Recommendation
当使用提供自动模型绑定功能的框架时,最佳做法是控制要绑定到模型对象的属性,这样,即使攻击者能够
确定该模型或嵌套类的其他未暴露属性,他们也无法绑定 HTTP 请求参数的任意值。 根据所使用的框架,可
以采用不同的方法控制模型绑定过程: Spring MVC: 可以控制绑定过程中要使用和忽略的 HTTP 请求参
数。 在使用 @ModelAttribute 注释参数的 Spring MVC 应用程序中,可以配置绑定器以控制应绑定的属
性。 为此,可以对方法进行 @InitBinder 注释,以便框架注入对 Spring 模型绑定器的引用。可以配置
Spring 模型绑定器,以使用 setAllowedFields 和 setDisallowedFields 方法控制属性绑定过程。扩
展 BaseCommandController 的 Spring MVC 应用程序可以替代 initBinder(HttpServletRequest
request, ServletRequestDataBinder binder) 方法,以获取对 Spring 模型绑定器的引用。 示例 2
: 将 Spring 模型绑定器 (3.x) 配置为禁止绑定敏感属性:
final String[] DISALLOWED_FIELDS = new String[]{“details.role”, “details.age”,
“is_admin”};
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields(DISALLOWED_FIELDS);
}
示例 3: 将 Spring 模型绑定器 (2.x) 配置为禁止绑定敏感属性:
@Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder
binder) throws Exception {
binder.setDisallowedFields(new String[]{“details.role”, “details.age”,
“is_admin”});
}
在使用 @RequestBody 注释参数的 Spring MVC 应用程序中,绑定过程由 HttpMessageConverter 实例
处理,这些实例使用 Jackson 和 JAXB 等库将 HTTP 请求正文转换为 Java 对象。这些库提供了注释来控制
应允许或禁止的字段。例如,对于 Jackson JSON 库,可以使用 @JsonIgnore 注释禁止将某个字段绑定到
请求。 示例 4: 某个控制器方法使用 @RequestBody 注释将 HTTP 请求绑定到 Employee 类实例。
@RequestMapping(value=“/add/employee”, method=RequestMethod.POST,
consumes=“text/html”)
public void addEmployee(@RequestBody Employee employee){
// Do something with the employee object.
}
应用程序使用默认的 Jackson HttpMessageConverter 将 JSON HTTP 请求绑定到 Employee 类。为了
禁止绑定 is_admin 敏感字段,使用了 @JsonIgnore 注释:
public class Employee {
@JsonIgnore
private boolean is_admin;
…
// Public Getters and Setters
…
}
注意: 有关如何配置 Jackson 和 JAXB 注释的更多详细信息,请查看下面的 REST 框架信息。 Apache
Struts: Struts 1 和 2 仅将 HTTP 请求参数绑定到具有关联的公共 setter 访问器的 Actions 或
ActionForms 属性。如果某个属性不应绑定到请求,则应将其 setter 设置为私有。 示例 5: 配置私有
setter 以便 Struts 框架不会自动绑定任何 HTTP 请求参数:
private String role;
private void setRole(String role) {
this.role = role;
}
REST 框架: 大多数 REST 框架会自动将内容类型为 JSON 或 XML 的 HTTP 请求正文绑定到模型对象。根据
用于 JSON 和 XML 处理的库,可以采用不同的方法控制绑定过程。以下是 JAXB (XML) 和 Jackson (JSON)
的一些示例: 示例 6: 使用 Oracle 的 JAXB 库从 XML 文档绑定的模型可以通过不同的注释控制绑定过程,
例如 @XmlAccessorType、 @XmlAttribute、 @XmlElement 和 @XmlTransient。可以告诉绑定器默认
不绑定任何属性,具体方法为:使用值为 XmlAccessType.NONE 的 @XmlAccessorType 注释对模型进行
注释,然后使用 @XmlAttribute 和 @XmlElement 注释选择应绑定的字段:
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class User {
private String role;
private String name;
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
示例 7: 使用 Jackson 库从 JSON 文档绑定的模型可以通过不同的注释控制绑定过程,例如 @JsonIgnore
、 @JsonIgnoreProperties、 @JsonIgnoreType 和 @JsonInclude。可以通过对某些属性进行
@JsonIgnore 注释,告诉绑定器忽略这些属性。
public class User {
@JsonIgnore
private String role;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
防止出现批量分配漏洞的另一方法是使用将 HTTP 请求参数绑定到 DTO 对象的分层体系结构。 DTO 对象仅
用于该目的,仅暴露 Web 表单或 API 合约中定义的属性,然后将这些 DTO 对象映射到域对象,在这里,可
以定义剩余的私有属性。
示例1,这种是针对get方式:
未处理前产生的问题
解决后如下
示例2,这种是针对@RequestBody这种:
未处理前产生的问题
解决后如下