如何限制Spring SpEL表达式范围以避免代码注入风险?
限制Spring SpEL执行范围,避免恶意注入
要实现仅允许从对象取值、阻断方法调用/反射等危险操作,核心是使用SimpleEvaluationContext替代默认的StandardEvaluationContext——它是Spring专门为安全场景设计的轻量级上下文,默认限制了方法调用、反射访问等风险操作,只支持属性访问和简单表达式运算。
具体实现步骤
1. 用SimpleEvaluationContext替代StandardEvaluationContext
把你的代码改成如下形式,使用只读数据绑定模式的SimpleEvaluationContext:
Test test = new Test("XYZ", 999); ExpressionParser expressionParser = new SpelExpressionParser(); // 创建只读上下文,仅允许属性访问和简单比较/运算 EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); // 允许的表达式:访问属性并做比较 Expression allowedExpr = expressionParser.parseExpression("value eq 'XYZ'"); System.out.println(allowedExpr.getValue(context, test)); // 输出true // 危险表达式会被拦截:尝试调用方法将抛出异常 Expression forbiddenExpr = expressionParser.parseExpression("''.getClass().forName('java.lang.Runtime').getMethods()[6]"); try { forbiddenExpr.getValue(context, test); } catch (EvaluationException e) { System.out.println("危险表达式被拦截:" + e.getMessage()); }
2. 细粒度控制允许访问的属性
如果需要进一步限制只能访问特定属性,可以自定义PropertyAccessor,仅开放指定属性:
class RestrictedPropertyAccessor implements PropertyAccessor { // 仅允许访问以下属性 private final Set<String> allowedProperties = Set.of("value", "number"); @Override public Class<?>[] getSpecificTargetClasses() { return new Class[]{Test.class}; } @Override public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { return allowedProperties.contains(name); } @Override public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { if (!canRead(context, target, name)) { throw new AccessException("属性 " + name + " 不允许访问"); } try { Field field = Test.class.getDeclaredField(name); field.setAccessible(true); return new TypedValue(field.get(target)); } catch (NoSuchFieldException | IllegalAccessException e) { throw new AccessException("无法访问属性 " + name, e); } } @Override public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return false; // 禁止修改属性 } @Override public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { throw new AccessException("禁止修改属性"); } }
然后将自定义Accessor添加到上下文:
EvaluationContext context = SimpleEvaluationContext.builder() .withPropertyAccessors(new RestrictedPropertyAccessor()) .build();
此时尝试访问非允许属性会直接抛出异常。
关键说明
- SimpleEvaluationContext的
forReadOnlyDataBinding()模式默认禁止方法调用、构造器调用、反射访问,仅支持属性访问、算术/比较运算等安全操作。 - 绝对不要在不可信表达式场景下使用StandardEvaluationContext,它默认无任何限制,极易被恶意利用。
内容的提问来源于stack exchange,提问作者woytech
相关产品推荐
相关产品推荐

