You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用EL表达式防代码注入?JSF中ValueExpression遭Fortify告警

咱们先搞清楚Fortify告警的原因,再一步步解决EL注入的问题。

为什么HP Fortify会告警?

你的createValueExpression方法接收的ev参数如果是用户可控的输入(比如来自前端表单、URL参数、用户上传的数据),就会触发这个告警。因为EL表达式是会在运行时被解析并执行的,攻击者可以构造恶意的EL代码,比如:

#{T(java.lang.Runtime).getRuntime().exec('rm -rf /')}

这种表达式会直接调用系统命令,造成严重的安全问题。Fortify检测到你把外部输入直接传入了EL表达式创建方法,认为存在代码注入风险。

如何防止EL表达式注入?

这里给你几个实用的方案,按优先级排序:

1. 绝对避免用户直接控制EL表达式字符串

最安全的做法是:不要让用户提供完整的EL表达式。所有的EL表达式应该是后端预定义的(比如常量、配置文件里的固定值),或者由后端逻辑生成,完全不依赖用户输入。

比如,如果你的需求是让用户选择某个bean的属性来展示,不要让用户输入#{user.name},而是让用户选择name这个属性名,后端自己拼接成安全的表达式,同时校验属性名的合法性。

2. 对用户可控的表达式部分做严格校验

如果必须用到用户输入作为表达式的一部分(比如属性名、参数),一定要做白名单校验:

  • 用正则表达式限制输入只能是字母、数字、下划线,比如^[a-zA-Z0-9_]+$
  • 禁止包含EL的特殊语法:比如()(方法调用)、T()(访问类)、new(创建对象)、;(多语句)、#{/}(表达式边界,避免用户嵌套表达式)

举个例子,在你的createValueExpression方法里加入校验:

public static final ValueExpression createValueExpression(
        final FacesContext context, 
        final ELContext elContext, 
        final String ev, 
        final Class classType) {
    // 校验EL表达式是否符合安全格式,比如只允许#{bean.property}结构
    if (!ev.matches("#\\{[a-zA-Z0-9_]+\\.[a-zA-Z0-9_]+\\}")) {
        throw new IllegalArgumentException("Invalid or unsafe EL expression");
    }
    return context.getApplication().getExpressionFactory()
            .createValueExpression(elContext, ev, classType);
}

3. 配置EL安全限制

大部分EL实现(比如Apache EL,JSF常用的底层EL引擎)都支持安全配置,禁用危险的表达式特性:

通过web.xml配置

添加以下参数来限制EL的权限:

<context-param>
    <param-name>org.apache.el.parser.SKIP_IDENTIFIER_CHECK</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>org.apache.el.security.ALLOW_METHOD_INVOCATION</param-name>
    <param-value>false</param-value>
</context-param>
<context-param>
    <param-name>org.apache.el.security.ALLOW_CLASS_LOADING</param-name>
    <param-value>false</param-value>
</context-param>

这些配置会禁止EL调用方法、加载危险类,从根源上减少注入风险。

自定义Secure ELProcessor

如果用的是Tomcat 8+,可以自定义ELProcessor并设置安全规则:

ELProcessor elProcessor = new ELProcessor();
elProcessor.getELManager().addELResolver(new SecureELResolver());
// 或者禁用特定功能
elProcessor.getELManager().setBeanResolver(new RestrictedBeanResolver());

这种方式可以更细粒度地控制EL能访问的类和方法。

4. 避免动态构造EL表达式,直接操作对象

如果你的需求是动态访问bean的属性或方法,尽量不要构造EL字符串,而是直接通过ELContext获取bean实例,然后用反射或getter方法访问:

// 直接获取bean实例
UserBean userBean = (UserBean) elContext.getELResolver().getValue(elContext, null, "userBean");
// 访问属性
String username = userBean.getUsername();

这种方式完全避免了动态EL表达式的风险,比构造字符串更安全。

针对你的代码的具体改进建议

如果ev参数确实是用户可控的,一定要在方法里加入校验逻辑,拒绝不符合安全格式的表达式。另外,尽量把表达式的生成逻辑放在后端,不要让用户输入完整的表达式。

比如,如果用户只需要指定属性名,你可以修改方法,让用户传入属性名,后端自己拼接成安全的表达式:

public static final ValueExpression createSafeValueExpression(
        final FacesContext context, 
        final ELContext elContext, 
        final String beanName,
        final String propertyName,
        final Class classType) {
    // 校验beanName和propertyName的合法性
    if (!beanName.matches("[a-zA-Z0-9_]+") || !propertyName.matches("[a-zA-Z0-9_]+")) {
        throw new IllegalArgumentException("Invalid bean or property name");
    }
    String ev = String.format("#{%s.%s}", beanName, propertyName);
    return context.getApplication().getExpressionFactory()
            .createValueExpression(elContext, ev, classType);
}

内容的提问来源于stack exchange,提问作者Ashutosh Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:18:14