今天同事提了一个bug,编辑一条记录时,发现某个字段的值为空,查看网页源代码发现原来这个字段的值带有特殊符号",如:value=""abc"",居然是XSS漏洞。难道SpringMVC框架默认没有对用户输入的特殊字符进行处理的?然后在网上找了很多关于SpringMVC防止XSS漏洞和SQL注入攻击的文章,发现内容大多重复,而且提供的预防方法也不是非常准确,有的方法根本无效。下面通过实验证明一些方法的可行性。

  首先,使用SpringMVC搭建一个简单的应用,从一个表单输入username参数值,提交之后在另一个页面的表单中显示。工程结构图如下:

QQ截图20151224170346.jpg

  控制器代码如下:

@Controller
@RequestMapping("/username")
public class UserNameController {

    @RequestMapping("/index")
    public String index() {
        return "index";
    }

    @RequestMapping("/show")
    public String show(Model model, HttpServletRequest request) {
        String username = request.getParameter("username");
        model.addAttribute("username", username);
        return "show";
    }
}

  提交表单

<form method="post" action="show.do">
     <input type="text" name="username" value="">
     <input type="submit" value="提交">
</form>

  显示表单

<form>
    用户名为:<input type="text" value="${username}">
</form>

一、默认测试

  基于最基本的SpringMVC工程进行测试,该工程目前不做任何防止XSS漏洞攻击的处理和配置,测试目标:SpringMVC是否自动处理参数中的特殊字符。
  打开index.do页面,输入括号中的值("&'ni'");其中包括5个特殊符号:<>&'"
  实现结果如下图所示:

QQ截图20151224171721.jpg

QQ截图20151224171738.jpg

  从上图可以看出show.do无法正常显示提交的值,使用浏览器调试工具查看源码,如下图:
QQ截图20151224171817.jpg

  因此我们可以得出结论,HttpServletRequest对象获取参数时,没有对特殊字符进行处理。为了测试SpringMVC框架内部是否处理特殊字符,我们在UserNameController中增加一个函数,使用@RequestParam获取参数,代码如下:

@RequestMapping("/show1")
public String show1(Model model, @RequestParam String username) {
    model.addAttribute("username", username);
    return "show";
}

  测试结果还是没有变化,仍然对包含特殊字符的参数值无法正常显示,如下图:

QQ截图20151224172956.jpg

  至此,我们可以得出一个基本结论,SpringMVC框架默认不处理XSS问题

二、在web.xml中配置defaultHtmlEscape参数

  在web.xml中增加配置代码:

<context-param>
    <param-name>defaultHtmlEscape</param-name>
    <param-value>true</param-value>
</context-param>

  经测试,无效!

三、在包含form的jsp页面中添加

<spring:htmlEscape defaultHtmlEscape="true" />

  经测试,无效!

四、直接在form中的元素中添加

<form:input path="someFormField" htmlEscape="true" />
或
<form:form htmlEscape="true">

  经测试,无效!

五、JSTL输出

<form>
    用户名为:<input type="text" value="${fn:escapeXml(username)}">
    用户名为:<input type="text" value="<c:out value='${username}' htmlEscape='true'/>">    //escapeXml默认为true
</form>

  经测试,可以正常显示包含特殊符号的参数值,显示结果如下图:

QQ截图20151225091943.jpg

  上述两个标签均使用了org.apache.taglibs.standard.tag.common.core.Util类的escapeXml方法,将>, <, &, ", ' 5个特殊符号进行处理。该方法的源码如下:

/**
 * Performs the following substring replacements
 * (to facilitate output to XML/HTML pages):
 *
 *    & -> &amp;
 *    < -> &lt;
 *    > -> &gt;
 *    " -> &#034;
 *    ' -> &#039;
 *
 * See also OutSupport.writeEscapedXml().
 */
public static String escapeXml(String buffer) {
    int start = 0;
    int length = buffer.length();
    char[] arrayBuffer = buffer.toCharArray();
    StringBuffer escapedBuffer = null;

    for (int i = 0; i < length; i++) {
        char c = arrayBuffer[i];
        if (c <= HIGHEST_SPECIAL) {
            char[] escaped = specialCharactersRepresentation[c];
            if (escaped != null) {
                // create StringBuffer to hold escaped xml string
                if (start == 0) {
                    escapedBuffer = new StringBuffer(length + 5);
                }
                // add unescaped portion
                if (start < i) {
                    escapedBuffer.append(arrayBuffer,start,i-start);
                }
                start = i + 1;
                // add escaped xml
                escapedBuffer.append(escaped);
            }
        }
    }
    // no xml escaping was necessary
    if (start == 0) {
        return buffer;
    }
    // add rest of unescaped portion
    if (start < length) {
        escapedBuffer.append(arrayBuffer,start,length-start);
    }
    return escapedBuffer.toString();
}

  本文通过实践证明,得到了可以正确显示包含特殊字符值的方法。这样可以避免因为特殊字符的输出扰乱页面整体结构,同时也可以防止脚本攻击。上面提到的方法均未在输入过程中对值进行处理,仅仅在输出时进行了转义处理。个人觉得这样做比较合理,因为系统需要处理的是用户输入的真实数据,只有在页面显示的时候适当处理即可。

文章作者:admin
本文链接:https://javatech.wang/index.php/archives/49/
版本所有 ©转载时必须以链接形式注明作者和原始出处