如何防止数据库字段注入?

时间:2019-02-19 10:51:19

标签: hibernate spring-mvc jpa

我有一个模型,其描述方式如下:

@Table
@Entity
@Data
@Builder
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class User {
    // ...skipped...
    private String foo;
}

我还有一个 service ,它具有创建新实体的方法:

@Service
public class UserService {
    // ...skipped...
    public User createUser(User user) {
        Date currentDate = new Date();
        user.setCreated(currentDate);
        userRepository.save(user);
        return user;
    }
}

我还有一个 controller 和一个映射方法:

@Controller
@RequestMapping(UserRouteRegistry.FIRST_LEVEL + "/*")
public class UserController {
    // ...skipped...
    @PostMapping(UserRouteRegistry.SIGN_UP)
    public String signUp(
            @ModelAttribute("user") @Validated(User.CreateUserGroup.class) User user,
            BindingResult result,
            WebRequest request,
            RedirectAttributes redirectAttributes
    ) {
    // ...mercellessly skipped...
                userRegistered = userService.createUser(user);
    // ...mercellessly skipped...
    }

一切都很简单,对吧?

我在网络表单中没有foo字段,并且我不需要网络用户来设置此字段,它应该是一些真正的私有字段,不受网络用户的影响直接。

发出自定义POST请求时,我看到正在创建一个新实体,其中的foo字段由请求中设置的数据填充:

POST /user/sign-up HTTP/1.1
Host: 127.0.0.1:8080
Origin:  http://127.0.0.1:8080
Content-Type: application/x-www-form-urlencoded
Referer:  http://127.0.0.1:8080/user/sign-up
Cookie:  SL_G_WPT_TO=ru; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; BL_D_PROV=; BL_T_PROV=; JSESSIONID=E1190293BD183C647245BAE03E6DCDDA
cache-control: no-cache
Postman-Token: e572bccf-ff0a-4b0b-a181-dd0aa20dca99
foo=13
> SELECT foo FROM user ORDER BY id DESC LIMIT 1 \G
*************************** 1. row ***************************
foo: 13
1 row in set (0.0005 sec)

是的,这似乎很正确,但是有什么办法可以以某种方式防止呢?

service 级别上为{private”字段分配null值?那将是非常乏味的,但是它将起作用。在 model 级别的此字段上设置一些@annotation(顺便说一下,应该是什么注释?)?我不确定这是否合乎逻辑。在 controller 级别上过滤参数,在某处列出所有允许的参数?也许是这样吗?

正确的方法是什么?

谢谢!

3 个答案:

答案 0 :(得分:1)

您可以在反序列化时忽略该字段

@JsonProperty(access = Access.READ_ONLY)
private String zaloopa;

答案 1 :(得分:1)

将模型和DTO(在前端和后端控制器之间转移的DTO)分开是一个好习惯,因为有时可能会有更多的事情,只有一个字段可以排除或处理不同的问题。也可能是在其他上下文中需要该字段,因此不能在模型本身中将其排除。

因此,当您的控制器现在接收User模型时,它应该接收UserDTO,该模型仅具有所需的允许字段。

这当然使事情变得更加困难,因为然后您需要在UserDTO之间映射User。但幸运的是,也有ModelMapper之类的库来处理。

答案 2 :(得分:1)

如果您要发布表单数据,则可以按照以下方式进行操作,即,创建一个用@InitBinder方法注释的方法,然后在提供的{{ 1}}实例。

此方法可以添加到您的控制器,Spring MVC控制器建议或两者中。对于所有实体共享的字段,您可以添加控制器建议,然后在相关控制器上注册其他实体特定的字段。

WebDataBinder

如果您要发布 JSON数据,则可以通过杰克逊注释来实现。您可以将这些字段直接放置在实体的字段中,也可以使用Jackson的mixin类来避免因网络层问题而“污染”域模型。

注释实体或mixin中的字段:

@InitBinder()
public void initBinder(WebDataBinder binder) {
    binder.setDisallowedFields(new String[] { "id", "version" });
}

要注册混音:

@JsonProperty(access = Access.READ_ONLY)
private String myField;