spring SPEL注入使用SPEL的安全问题

vxf3dgd4  于 2023-09-29  发布在  Spring
关注(0)|答案(1)|浏览(134)

我们计划在基于SaaS的产品中使用SPEL来执行条件表达式。我的要求是使用求值条件,用户可以使用变量,常量和标准函数编写条件表达式。到目前为止,我已经使用SpelExpressionParser()解析器来解析表达式,然后使用StandardEvaluationContext()创建上下文,然后使用expression evaluate来计算表达式。

ExpressionParser parser = new SpelExpressionParser();
    Expression exp = parser.parseExpression("(#Tag.matches('52.*') && #Name.toUpperCase().matches('DEF.*') ");
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("Tag", "57345");
    context.setVariable("Name", "defg");
    Boolean result = (Boolean) exp.getValue(context);

这对我的所有需求都很好,但我的安全团队对使用StandardEvaluationContext()有顾虑。StandardEvaluationContext()可用于SpEL注入,并可用于使用Java RMI的远程方法调用。以下是一些安全问题:
SpEL注入攻击:https://0xn3va.gitbook.io/cheat-sheets/framework/spring/spel-injection
关于EL注入攻击的OSWASP文章:https://owasp.org/www-community/vulnerabilities/Expression_Language_Injection
我们被建议使用SimpleEvaluationContext(),我看到了我不能使用标准java函数和用户定义函数的限制(可能我不清楚是否有方法使用它)。
我计划在我的代码中使用最新的2.7.x版本的Spring框架和Java 11。
有没有人可以引导我明白:
1.使用StandardEvaluationContext()的更安全的方法,我可以禁用某些Java函数,如RMI和系统函数,以便我可以安全地在我们的产品中使用?
1.如果SimpleEvaluationContext()是正确的方法,那么有人可以帮助我使用标准函数和用户定义函数吗?

mgdq6dx1

mgdq6dx11#

了解一些细节后,您可以更改方法,在这种情况下避免使用SPEL,而是使用Java Bean Validation并创建一个自定义的。
大概是这样的:
使用TargetValidatorValues注解声明为TYPE

@Documented
@Constraint(validatedBy = ValidatorValuesImpl.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatorValues {

  String message() default "All good";

  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};

}

创建一个它的impl,你将把条件检查到isValid方法中,这里作为例子,我把你的情况从评论中,顺便说一下,这个方法总是会返回true,因为我们把它用作value generator而不是constraint validator

public class ValidatorValuesImpl implements ConstraintValidator<ValidatorValues, Dataset> {

  @Override
  public void initialize(ValidatorValues constraintAnnotation) {
    ConstraintValidator.super.initialize(constraintAnnotation);
  }

  @Override
  public boolean isValid(Dataset value, ConstraintValidatorContext context) {
    value.setPromotion((value.getAge() > 30) && (value.getAge() < 50));
    //.... others conditions

    return true;
  }
}

数据集也来自注解示例,这里我在promotion字段设置@JsonIgnore,因为它是基于用户输入动态设置的。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ValidatorValues
public class Dataset {

  private String name;

  private Integer age;

  private String phone;

  private String email;

  @JsonIgnore
  private Boolean promotion;
}

还有控制器层,当请求到达后端时,它将被使用:

@RestController
@RequestMapping("/endpoint")
public class DataSetController {

  private final Validator validator;

  public DataSetController(Validator validator) {
    this.validator = validator;
  }

  @PostMapping("/test")
  ResponseEntity<String> createDataset(@RequestBody @Valid Dataset eventRequestBody) {
    validator.validate(eventRequestBody);

    return ResponseEntity.ok("promotion: " + eventRequestBody.getPromotion().toString());
  }
}

一些测试用例 Postman 请求/响应:
1.如果年龄未通过验证:应返回促销false

1.如果年龄通过验证:应返回促销true

相关问题