JSP <c:out>标签中的escapeXML方法转义了哪些字符?

showtooltip 2020-06-25

JSP的<c:out>标签有如下属性:

属性 描述 是否必要 默认值
value 要输出的内容
default 输出的默认值 主体中的内容
escapeXml 是否忽略XML特殊字符 true

 

value,default,以及escapeXML。

这里要讲述的就是escapeXML。

从上面的表格中可以看出,escapeXML属性默认值是true

 

一般我们用<c:out>标签的时候,最常用的就是value属性,对于escapeXml属性,却常常的忽略。

escapeXml的意思,就是对value值进行过滤,将其中的特殊字符给转义掉。

因为escapeXml的默认值是true的缘故,也就是默认开启了对value值的转义,这就默认的,让你在使用<c:out>标签时,提供了安全性。

许多没有意识到escapeXml优点的开发者,往往嫌弃<c:out>太麻烦,于是在页面输出值中,直接使用了el表达式。

我们看两个例子。

<body>

	<% 
	request.getServletContext().setAttribute("name", "hello escapeXml");
	 %>
	<div>
		<c:out value="${name}" escapeXml="true"></c:out>
	</div>
	<div>
		${name}
	</div>
	
</body>

 

以上的代码,页面输出结果是:

hello escapeXml

hello escapeXml

确实,在这种情况下,用<:out>以及用el表达式直接输出,结果是一样的。

但是稍微改一下,代码改动为:

<body>

	<% 
	request.getServletContext().setAttribute("name", "<script>document.write('hello escapeXml')</script>");
	 %>
	<div>
		<c:out value="${name}" escapeXml="true"></c:out>
	</div>
	<div>
		${name}
	</div>
	
</body>

 

页面的输出结果就变成了

<script>document.write('hello escapeXml')</script>

hello escapeXml

 

第一行,带有script标签的,确实是我们想要输出的内容,用<c:out>也完美的输出了!

第二行,直接使用el表达式,结果却是hello escapeXml,很明显,直接使用el表达式,实际上,执行了<script>中的js语句。

 

这就是漏洞的来源,可被用于XSS跨站攻击。

 

由此可见,在JSP页面输出结果,用<c:out>明显是安全的,原因在于escapeXml方法转义了可能影响安全的特殊字符,即便你在编写代码的时候,忘记给<c:out>加上escapeXml,也不要紧,因为escapeXml缺省的默认值是true。

 

那么,escapeXml方法到底转义了哪些字符呢?

关于这个,可以从JSTL的源码包中找到答案,主要看的OutSupport这个类。

package org.apache.taglibs.standard.tag.common.core;

import java.io.IOException;
import java.io.Reader;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;

/**
 * <p>Support for handlers of the &lt;out&gt; tag, which simply evalutes and
 * prints the result of the expression it's passed.  If the result is
 * null, we print the value of the 'default' attribute's expression or
 * our body (which two are mutually exclusive, although this constraint
 * is enforced outside this handler, in our TagLibraryValidator).</p>
 *
 * @author Shawn Bayern
 */
public class OutSupport extends BodyTagSupport {

    /*
     * (One almost wishes XML and JSP could support "anonymous tags,"
     * given the amount of trouble we had naming this one!)  :-)  - sb
     */

    //*********************************************************************
    // Internal state

    protected Object value;                     // tag attribute
    protected String def;			// tag attribute
    protected boolean escapeXml;		// tag attribute
    private boolean needBody;			// non-space body needed?

    //*********************************************************************
    // Construction and initialization

    /**
     * Constructs a new handler.  As with TagSupport, subclasses should
     * not provide other constructors and are expected to call the
     * superclass constructor.
     */
    public OutSupport() {
        super();
        init();
    }

    // resets local state
    private void init() {
        value = def = null;
        escapeXml = true;
	needBody = false;
    }

    // Releases any resources we may have (or inherit)
    public void release() {
        super.release();
        init();
    }


    //*********************************************************************
    // Tag logic

    // evaluates 'value' and determines if the body should be evaluted
    public int doStartTag() throws JspException {

      needBody = false;			// reset state related to 'default'
      this.bodyContent = null;  // clean-up body (just in case container is pooling tag handlers)
      
      try {
	// print value if available; otherwise, try 'default'
	if (value != null) {
            out(pageContext, escapeXml, value);
	    return SKIP_BODY;
	} else {
	    // if we don't have a 'default' attribute, just go to the body
	    if (def == null) {
		needBody = true;
		return EVAL_BODY_BUFFERED;
	    }

	    // if we do have 'default', print it
	    if (def != null) {
		// good 'default'
                out(pageContext, escapeXml, def);
	    }
	    return SKIP_BODY;
	}
      } catch (IOException ex) {
	throw new JspException(ex.toString(), ex);
      }
    }

    // prints the body if necessary; reports errors
    public int doEndTag() throws JspException {
      try {
	if (!needBody)
	    return EVAL_PAGE;		// nothing more to do

	// trim and print out the body
	if (bodyContent != null && bodyContent.getString() != null)
            out(pageContext, escapeXml, bodyContent.getString().trim());
	return EVAL_PAGE;
      } catch (IOException ex) {
	throw new JspException(ex.toString(), ex);
      }
    }


    //*********************************************************************
    // Public utility methods

    /**
     * Outputs <tt>text</tt> to <tt>pageContext</tt>'s current JspWriter.
     * If <tt>escapeXml</tt> is true, performs the following substring
     * replacements (to facilitate output to XML/HTML pages):
     *
     *    & -> &amp;
     *    < -> &lt;
     *    > -> &gt;
     *    " -> &#034;
     *    ' -> &#039;
     *
     * See also Util.escapeXml().
     */
    public static void out(PageContext pageContext,
                           boolean escapeXml,
                           Object obj) throws IOException {
        JspWriter w = pageContext.getOut();
	if (!escapeXml) {
            // write chars as is
            if (obj instanceof Reader) {
                Reader reader = (Reader)obj;
                char[] buf = new char[4096];
                int count;
                while ((count=reader.read(buf, 0, 4096)) != -1) {
                    w.write(buf, 0, count);
                }
            } else {
                w.write(obj.toString());
            }
        } else {
            // escape XML chars
            if (obj instanceof Reader) {
                Reader reader = (Reader)obj;
                char[] buf = new char[4096];
                int count;
                while ((count = reader.read(buf, 0, 4096)) != -1) {
                    writeEscapedXml(buf, count, w);
                }
            } else {
                String text = obj.toString();
                writeEscapedXml(text.toCharArray(), text.length(), w);
            }
        }
    }

   /**
     *
     *  Optimized to create no extra objects and write directly
     *  to the JspWriter using blocks of escaped and unescaped characters
     *
     */
    private static void writeEscapedXml(char[] buffer, int length, JspWriter w) throws IOException{
        int start = 0;

        for (int i = 0; i < length; i++) {
            char c = buffer[i];
            if (c <= Util.HIGHEST_SPECIAL) {
                char[] escaped = Util.specialCharactersRepresentation[c];
                if (escaped != null) {
                    // add unescaped portion
                    if (start < i) {
                        w.write(buffer,start,i-start);
                    }
                    // add escaped xml
                    w.write(escaped);
                    start = i + 1;
                }
            }
        }
        // add rest of unescaped portion
        if (start < length) {
            w.write(buffer,start,length-start);
        }
    }
}

 

该类的out方法注释中,也已经阐明,会转义的字符包括:

	& -> &amp;
    < -> &lt;
    > -> &gt;
	" -> &#034;
	' -> &#039;

同时需参考Util这个类,在org.apache.taglibs.standard.tag.common.core包下。

用Java可以做网站吗?需要掌握哪些知识?
这个类,能提供WEB服务方面编程就行。 另外还有JSP,在JSP可以直接写Java代码,但不建议这么做,或者尽量少写,你可以使用JSP提供一些标签,做一些简单数据处理,不过更建议你使用EL...
「干货分享」打造高质量伪原创,轻轻松松上排名
几乎每一个站长都知道原创文章重要性,可是对于广大草根站长而言,原创就是个老大难问题,苦巴巴老半天,都写不出百十来个字。 于是不少站长寄希望于某某伪原创工具,又或者听别人讲方法,七拼八凑来...
wifi密码忘怎么办(找回WiFi密码方法步骤)
或者直接重置密码方法步骤。   1、登录到路由器。 这个很简单,直接在浏览器输入192.168.1.1,这里要注意,有的人登录路由器地址是192.168.3.1,出现路由器登录页面,输入路由器...
【食物语】一睁眼,发现我重生!⑪昆仑之境 ● BG● 食物语乙女向同人小说● 甜文有点小虐● ALL女少主● HE
进入其中便现出身形。   食魂们伫立在万象阵,身上发出金色光芒,却一动也不能动。 他们拼命地挣扎着,想要离开这个奇怪万象阵,想要回去,因为那个小姑娘还在里面,没有出来。   “别挣扎,这样只...
stopcasting整理:怀旧服圣骑士便捷宏命令(一个宏搞定6种祝福)
)或中立目标,或以上目标不在审判10码范围内。否则容易意外审判进入战斗。 == 还有部分人弄熊怪变身,需取消变身才能上马,则在上面加一行/cancelaura 熊怪形态 == PVE还可以在...
qqkongj写心情文章:我大概是一个天性就不讨喜
qqkongj收藏写心情文章,原作者:成为一株花   我大概是一个天性就不讨喜。 处女座都有挑剔毛病,以及骨子里冷淡拒人千里,虽然已经努力克制,但是和恋人相处过程多多少少也是无法避免...
pv是什么意思(提升pv4种方法
访问速度。 人们总是喜欢干净清爽和方便东西,试想你网站打开速度慢,等五六秒才打开,那访客几乎是没这个耐心,亦或者网站广告满天飞,各种弹窗,再或者配色十分涨眼(这方面,程序员某些,尤其很...
什么是凝聚力(提升团队凝聚力6个方法
目标,但迈向同一个目标过程,人与人之间方法,总是会产生分歧。 好比团队要开发一个项目,小张觉得用Python比较好,小王觉得用golang比较好,如果一直停留在这个层面争执,非但项目进度严重...
yahuoo:我看到当年试图强暴我而未遂强奸犯!!!
REALLY CANNOT BEAR IT!!! I must shout out!!! 虽然他被判若干年监禁,但是由于他可怜老母亲,众人对他一片哀怜之声,丝毫没有想到他受害者,尤其是那些“既...
java定时器使用(Timer)
任务,并且设定任务间隔时间period void    schedule(TimerTask task, long delay) //参数Date日期,换成long形式...
帕塔比乔伊斯一封信,来自1995年11月瑜伽杂志。
。 (力量瑜伽,1995年1月/ 2月)。“力量瑜伽”标题本身降低深度,瑜伽目的和系统方法,我上师--克里希纳马扎亚告诉我:力量是上帝属性。它不是收集自我。部分瑜伽方法与它们...
qqkongj摘抄写心情美文佳句(建立亲密关系)
,这些对任何人来说都不是难事。但是如果要感情坚持很久,就必须找到适合彼此相处又不妨碍自己与自己相处方法,找到这个方法对于年轻人或者大多数人来说真很难。 人都是个体,个体存在势必带着自我标签...