XSS漏洞产生后必然会有相关的输入/输出,因此我们只需快速找到这些输入/输出点,即可快速地进行跟踪发现漏洞。输入在Java中通常使用“request.getParameter(param)
”或“${param}
”获取用户的输入信息。输出主要表现为前端的渲染,我们可以通过定位前端中一些常见的标识来找到它们,然后根据后端逻辑来判断漏洞是否存在
1、JSP表达式
“<%=变量%>
”是“<%out.println 变量;%>
”的简写方式,“<%=%>
”用于将已声明的变量或表达式输出到外网页中
通过“request.getParameter
”获取msg传入的值,然后通过“<%=msg%>
”将其输出到网页中:
<% String msg = request.getParameter("msg");%>
<%= msg %>
2、EL
EL是为了使JSP写起来更加简单。EL的灵感来自于ECMAScript和XPath表达式语言,它提供了在JSP中简化表达式的方法,使得JSP的代码更加简化。例如:“<%=request.getParameter("username")%>
”等价于“${param.username}
”。JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能
<c:out>
标签用来显示一个表达式的结果,与<%=%>
作用相似,它们的区别是,<c:out>
标签可以直接通过“.
”操作符来访问属性,如下:
<c:out value="${requestScope.message}" />
<c:out value="${5 + 3}" />
<c:if>
标签用来判断表达式的值,如果表达式的值为true,则执行其主体内容:
<c:set var="loggedIn" value="true" />
<c:if test="${loggedIn eq 'true'}">
<p>Welcome, User!</p>
</c:if>
<c:if test="${loggedIn eq 'false'}">
<p>Please log in to continue.</p>
</c:if>
<c:forEach>
标签的作用是迭代输出标签内部的内容。它既可以进行固定次数的迭代输出,也可以依据集合中对象的个数来决定迭代的次数:
<c:forEach var="item" items="${myList}">
<p>${item}</p>
</c:forEach>
3、ModelAndView类的使用
ModelAndView类用来存储处理完成后的结果数据,以及显示该数据的视图,其前端JSP页面可以使用“${参数}
”的方法来获取值:
@GetMapping("/hello")
public ModelAndView sayHello() {
String message = "Hello, Spring MVC!";
// 创建一个ModelAndView对象,并设置视图名称和模型数据
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("hello"); // 设置视图名称为"hello",这里假设存在名为"hello.jsp"的视图文件
// 添加模型数据
modelAndView.addObject("message", message);
return modelAndView;
}
4、ModelMap类的使用
Spring也提供了ModelMap类,这是java.util.Map实现的,可以根据模型属性的具体类型自动生成模型属性的名称
@GetMapping("/hello")
public String hello(ModelMap model) {
model.addAttribute("message", "Hello, World!");
return "hello";
}
5、Model类的使用
Model类是一个接口类,通过attribue()
添加数据,存储的数据域范围是requestScope
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", "Hello, World!");
return "hello";
}
通过这些常见语法的总结我们不难归纳出一些常见的关键字,通过这些关键字可以快速地定位至具有前后端交互功能的代码片段:
在审计中我们只需通过搜索特定的关键字找到数据的交互点,然后判断这些数据是否可控以及输出位置。当数据可控并且直接在浏览器页面输出时,可进一步构造XSS攻击代码
例如:如下是一段简单的导致反射型XSS代码,没对输出做处理。当攻击者输入恶意js语句时可触发:
@GetMapping("/reflect")
public static String input(String content) {
return content;
}
当我们构造如下的输入参数时:
http://127.0.0.1:8888/XSS/reflect?content=test%3Cscript%3E%0A%20%20%20%20window.onload=function()%20%7B%0A%20%20%20%20%20%20%20%20document.bgColor%20=%20%22black%22;%0A%20%20%20%20%20%20%20%20document.body.innerHTML=%22%3Cfont%20color=white%20size=40%3E%E4%BD%A0%E8%A2%ABXSS%E9%BB%91%E4%BA%86%EF%BC%81%3C/font%3E%22%0A%20%20%20%20%7D%0A%3C/script%3E
XSS攻击成功被触发:
存储型XSS与反射型XSS核心原理一致,都是将JavaScript通过程序输出到HTML页面中并交由浏览器引擎解析。相比于反射型XSS,存储型XSS危害更大。反射型XSS需构造恶意URL来诱导受害者点击,而存储型XSS由于有效载荷直接被写入了服务器中,且不需要将有效载荷输入到URL中,往往可以伪装成正常页面,迷惑性更强。因此存储型XSS漏洞对于普通用户而言很难及时被发现
网站程序中常见的存储型XSS攻击点一般有:文章编辑、用户留言、个性签名等
1、修复XSS攻击,一个有效的方案是将特殊字符做转义,对所有字符采用HTML实体编码,但是请记住这种方法有局限性:
例如,如下是一个对输入输出转义的方法:
private static String XssFilter(String content) {
content = StringUtils.replace(content, "&", "&");
content = StringUtils.replace(content, "<", "<");
content = StringUtils.replace(content, ">", ">");
content = StringUtils.replace(content, "\", """);
content = StringUtils.replace(content, "'", "'");
content = StringUtils.replace(content, "/", "/");
return content;
}
此时,尝试触发XSS:http://127.0.0.1:8888/XSS/filter?content=test%3Cscript%3Ealert(1)%3C/script%3E
没有弹窗,而是将输入文本以字符串的方式展示在页面:
2、采用Spring自带的方法会对特殊字符全转义
上述转义的方案,有一个更简单的实现方法,使用htmlEscape
方法:
@GetMapping("/safe1")
public static String safe1(String content) {
return HtmlUtils.htmlEscape(content);
}
3、对于富文本编辑器的XSS防御,一般采用白名单标签的方法,因为针对富文本的处理方式,需保留部分标签可以被解析使用:
public static String safe3(String content) {
Safelist whitelist = (new Safelist())
.addTags("p", "hr", "div", "img", "span", "textarea") // 设置允许的标签
.addAttributes("a", "href", "title") // 设置标签允许的属性, 避免如nmouseover属性
.addProtocols("img", "src", "http", "https") // img的src属性只允许http和https开头
.addProtocols("a", "href", "http", "https");
return Jsoup.clean(content, whitelist);
}
4、采用OWASP Java Encoder会对特殊字符全转义
public static String safe5(String content){
return Encode.forHtml(content);
}
5、使用ESAPI
ESAPI 是一个免费、开源的、网页应用程序安全控件库,它使程序员能够更容易写出更低风险的程序
public static String safe4(String content){
return ESAPI.encoder().encodeForHTML(content);
}
6、编写全局过滤器实现拦截,并在web.xml进行配置