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

控制器代码如下:
@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页面,输入括号中的值("
实现结果如下图所示:
从上图可以看出show.do无法正常显示提交的值,使用浏览器调试工具查看源码,如下图:
因此我们可以得出结论,HttpServletRequest对象获取参数时,没有对特殊字符进行处理。为了测试SpringMVC框架内部是否处理特殊字符,我们在UserNameController中增加一个函数,使用@RequestParam获取参数,代码如下:
@RequestMapping("/show1")
public String show1(Model model, @RequestParam String username) {
model.addAttribute("username", username);
return "show";
}
测试结果还是没有变化,仍然对包含特殊字符的参数值无法正常显示,如下图:

至此,我们可以得出一个基本结论,
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>
经测试,可以正常显示包含特殊符号的参数值,显示结果如下图:

上述两个标签均使用了org.apache.taglibs.standard.tag.common.core.Util类的escapeXml方法,将>, <, &, ", ' 5个特殊符号进行处理。该方法的源码如下:
/**
* Performs the following substring replacements
* (to facilitate output to XML/HTML pages):
*
* & -> &
* < -> <
* > -> >
* " -> "
* ' -> '
*
* 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();
}
本文通过实践证明,得到了可以正确显示包含特殊字符值的方法。这样可以避免因为特殊字符的输出扰乱页面整体结构,同时也可以防止脚本攻击。上面提到的方法均未在输入过程中对值进行处理,仅仅在输出时进行了转义处理。个人觉得这样做比较合理,因为系统需要处理的是用户输入的真实数据,只有在页面显示的时候适当处理即可。