`

ELResolver Escapes JSP EL Values To Prevent Cross-Site Scripting

    博客分类:
  • SSH
 
阅读更多
JspFactory.getDefaultFactory()返回null的异常解决办法:
unable to register a custom ELResolver http://stackoverflow.com/questions/2551517/gae-j-unable-to-register-a-custom-elresolver
I am not sure which servletcontainer GAE uses "under the hoods" (Jetty? Tomcat?), but this is recognizeable as a bug in Tomcat 6.x. A workaround is to force the loading of JspRuntimeContext yourself before getting the factory:
Class.forName("org.apache.jasper.compiler.JspRuntimeContext");
See if this or similar hack helps.

ELResolverhttp://pz0513.blog.51cto.com/443986/112605
能够自定义 EL 表达式求值的变量和属性解析行为。
对一个表达式进行求值时,参考与 ELContext 关联的 ELResolver,以对表达式的第一个变量进行初始解析。遇到 . 或 [] 操作符时也要参考它,但方法表达式中最后一个这样的操作符除外,因为在这种情况下解析规则是固定编码的。
例如,在 EL 表达式 ${employee.lastName} 中,ELResolver 确定 employee 引用的对象,以及获取该对象 lastName 属性的含义。
此类中的大多数方法都接受 base 和 property 参数。如果是变量解析(例如,确定 ${employee.lastName} 中 employee 引用的内容),则 base 参数将为 null,property 参数将总是为 String 类型。这种情况下,如果 property 不是 String,则 ELResolver 的行为未定义。
如果是属性解析,则 base 参数标识 base 对象,property 对象标识 base 上的属性。例如,在表达式 ${employee.lastName} 中,base 是 employee 变量解析的结果,property 是字符串 "lastName"。在表达式 ${y[x]} 中,base 是 y 变量解析的结果,property 是 x 变量解析的结果。
尽管只有一个 ELResolver 与 ELContext 关联,但对于任何给定的变量或属性解析,通常可以使用多个解析器。ELResolver 使用 CompositeELResolver 连接到一起,以定义丰富语义来计算表达式。
对于 #getValue、#getType、#setValue 和 #isReadOnly 方法,ELResolver 不负责解析所有可能的 (base, property) 对。实际上,大部分解析器将仅能处理一种类型的 base。要指示解析器成功地解析特定 (base, property) 对,它必须将 ELContext 的 propertyResolved 属性设置为 true。如果它无法处理给定对,则必须保持此属性不变。如果 propertyResolved 为 false,则调用者必须忽略该方法的返回值。
设计 #getFeatureDescriptors 和 #getCommonPropertyType 方法的主要目的是支持设计时工具,但它们也必须在运行时处理调用。可以使用 java.beans.Beans#isDesignTime 方法确定是否在设计时(或运行时)参考了解析器。
......
详解见原文

原文:http://pukkaone.github.io/2011/01/03/jsp-cross-site-scripting-elresolver.html
Cross-site scripting is a computer security vulnerability enabling an attacker to inject malicious code into a Web page that will be executed by the Web browser when other users view the page. If your Web application accepts data from the user and then outputs that data unaltered in HTML, then it is vulnerable because user-controlled data might contain executable code.

Since JSP 2.0, EL expressions can appear in the template text:

<h1>Hello, ${user.name}</h1>

Unfortunately, the JSP container does not escape expression values, so if the expression contains user-controlled data, then cross-site scripting is possible. JSTL provides a couple of ways to sanitize the output. The c:out tag escapes XML characters by default:

<c:out value="${user.name}"/>

Alternatively, the EL function fn:escapeXml also escapes XML characters:

${fn:escapeXml(user.name)}

The default option should be the safe option. That's a sensible engineering principle. If EL values are escaped by default, then you're protected from coders who forget to wrap expressions in c:out or fn:escapeXml.

Starting with JSP 2.1, a Web application can register a custom ELResolver. I'm going to present a custom ELResolver that escapes EL values, allowing you to use EL in JSPs while preventing cross-site scripting.

A custom servlet context listener registers the custom ELResolver when the application starts. To use it, define a listener in the web.xml file:

<listener>
  <listener-class>com.github.pukkaone.jsp.EscapeXmlELResolverListener</listener-class>
</listener> 

Disable escaping

When you register this custom ELResolver, all EL values will be escaped by default. If you want a JSP to programmatically output HTML, you can resort to a JSP scriptlet or JSP expression, unless the application configured scripting-invalid to true.

Another way uses a custom tag to surround JSP code in which EL values should not be escaped:

<%@ taglib prefix="enhance" uri="http://pukkaone.github.com/jsp" %>

<enhance:out escapeXml="false">
  I hope this expression returns safe HTML: ${user.name}
</enhance:out>

The escapeXml attribute is true by default. You must explicitly set it to false in the tag to disable escaping.

Details

The servlet context listener's contextInitialized method calls the JspApplicationContext.addELResolver method to register the custom ELResolver.

    public void contextInitialized(ServletContextEvent event) {
        JspFactory.getDefaultFactory()
                .getJspApplicationContext(event.getServletContext())
                .addELResolver(new EscapeXmlELResolver());
    }

The addELResolver method inserts the custom ELResolver into a chain of standard resolvers. When evaluating an expression, the JSP container consults the chain of resolvers in the following order, stopping at the first resolver to succeed:

ImplicitObjectELResolver
ELResolvers registered by the addELResolver method.
MapELResolver
ListELResolver
ArrayELResolver
BeanELResolver
ScopedAttributeELResolver
This presents a slight problem because the custom ELResolver wants to escape the value that would have resulted from consulting the chain. When asked for a value, the custom ELResolver invokes the chain of resolvers. The custom ELResolver is itself in the chain of resolvers, so before invoking the chain, it sets a flag telling itself to do nothing when its turn in the chain comes around.

    private boolean gettingValue;

    @Override
    public Object getValue(ELContext context, Object base, Object property)
        throws NullPointerException, PropertyNotFoundException, ELException
    {
        if (gettingValue) {
            return null;
        }

        gettingValue = true;
        Object value = context.getELResolver().getValue(
                context, base, property);
        gettingValue = false;

        if (value instanceof String) {
            value = EscapeXml.escape((String) value);
        }
        return value;
    }

There's a resolver in the chain before the custom ELResolver. This resolver, ImplicitObjectELResolver, will be invoked twice. First, before reaching the custom ELResolver, and again when the custom ELResolver invokes the chain. Multiple invocations of ImplicitObjectELResolver is harmless because ImplicitObjectELResolver had to fail in order for the custom ELResolver to be invoked. When the custom ELResolver invokes the chain, the ImplicitObjectELResolver will fail again.

A resolver indicates success by setting the propertyResolved property of the ELContext to true. When consulting the chain, one of the resolvers very likely set this property to true, so no other resolvers are invoked after returning from the custom ELResolver.
分享到:
评论
1 楼 nizen 2013-08-02  
一个比较纠结的问题是,resolver初衷是解决不能解析的base,但是在这里,目的是在已解析的基础上做一层封装。正是由于这个不同,最终导致在xss过滤前可能会出现递归调用resolver。pukkaone的做法是通过成员变量+threadLocal来识别是否已调用。这个有没更优雅的实现?

相关推荐

Global site tag (gtag.js) - Google Analytics