XSS之java代码审计篇(含修复措施)

xiao1star2025-04-18文章来源:SecHub网络安全社区


XSS之java篇

代码

前端代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>注册</title> </head> <body> <h1>欢迎注册</h1> <form class="form" id="form" action="register" method="post"> 账号:<input type="text" id="name" style="width:300px;height:30px" name="name" ><br> 密码:<input type="password" id="password" style="width:300px;height:30px" name="password"><br> <span id="msg">${registermodel.msg}</span> <input class="button" type="button" id="register" value="注册" > </form> </body> <script type="text/javascript" src="js/jquery-3.4.1.js"></script> <script type="text/javascript"> $("#register").click(function () { //获取表单中name和password的值 var name=$("#name").val(); var password=$("#password").val(); //判断name和password是否为空 if(isEmpty(name)||isEmpty(password)){ // alert("亲,账号密码不能为空!"); $("#msg").html("亲,账号密码不能为空!"); return; } $("#form").submit(); }) /* * 用于判断字符串是否为空 * 若为空返回true,反之返回false * */ function isEmpty(str){ return str == null || str.trim() == ""; } </script> </html>

后端代码

controller层

package com.controller; import com.entity.User; import com.entity.vo.MessageModel; import com.service.RegisterService; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @SuppressWarnings({"all"}) @WebServlet("/register") public class RegisterController extends HttpServlet { RegisterService registerService = new RegisterService(); @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接受前端数据 String name=request.getParameter("name"); String password=request.getParameter("password"); //2.调用service层的方法,返回消息模型对象 MessageModel RegisterModel=registerService.useregister(name,password); if(RegisterModel.getCode()==1){ //成功,则重定向到登录页面 request.getSession().setAttribute("register",RegisterModel.getObject());//存入用户信息 response.sendRedirect("login.jsp");//跳转到首页 /*getSession() *这是HttpServletRequest接口中的一个方法, *用于获取与当前请求关联的HttpSession对象(即会话)。 *如果当前请求没有关联的会话,则此方法会根据请求的规范(例如,是否启用了cookie, *或者URL中是否包含JSESSIONID)来创建一个新的会话,或者返回一个已存在的会话。 * */ /*setAttribute() *这是HttpSession接口中的一个方法,用于在会话中存储一个属性。 * 这个方法接受两个参数:属性的名称(name)和属性的值(value)。 * 在这个例子中,属性名称是"user",而属性值是通过messageModel.getObject()获取的。 * * */ } else{ //失败 request.setAttribute("registermodel",RegisterModel); request.getRequestDispatcher("register.jsp").forward(request,response); } } }

service层

package com.service; import com.entity.User; import com.entity.vo.MessageModel; import com.mapper.RegisterMapper; import com.mapper.UserMapper; import com.until.SqlSessionUtils; import com.until.StringUtil; import org.apache.ibatis.session.SqlSession; public class RegisterService { public MessageModel useregister(String username, String password) { //创建一个消息模型对象,用于判断注册(数据库插入)是否成功以及前端信息的回显 MessageModel messageModel = new MessageModel(); //回显数据 User u=new User(); u.setUsername(username); u.setPassword(password); messageModel.setObject(u); //参数的非空判断 if(StringUtil.isempity(username)||StringUtil.isempity(password)){ //参数为空,将状态码、提示信息、回显数据,返回消息模型 messageModel.setCode(0); messageModel.setMsg("亲!用户姓名或密码不能为空"); return messageModel; } //调用dao(mapper)层返回对象,执行相应的sql语句 SqlSession session= SqlSessionUtils.getSqlSession(); RegisterMapper registerMapper=session.getMapper(RegisterMapper.class); //调用dao层的方法 User user=registerMapper.selectByUsername(username); //判断用户对象是否为空 if(user!=null){ //用户名以存在 messageModel.setMsg("用户名已经存在"); messageModel.setCode(0); return messageModel; } //用户名不存在,添加用户 registerMapper.add(u); return messageModel; } }

mapper层

202407211612428.png

代码分析

  • 在service层中,只进行了注册账号密码是否为空的判断 if(StringUtil.isempity(username)||StringUtil.isempity(password))、数据库中是否存在要注册的用户名的判断if(user!=null)
  • 也就是说当攻击者注册的用户名类似于xss的危险语句,并登录后首页中会有用户名的显示,那么很有可能会造成xss

漏洞复现

  1. 在注册框中注册一个用户名为<script>alert(1)</script>的用户

202407211628574.png

  1. 数据库中成功插入这个用户

202407211631379.png

  1. 使用该用户进行登录

202407211628204.png

  1. 成功弹出弹窗

202407211627565.png

202407211627238.png

代码修复

输入端验证

输入验证是一种用于确保用户输入数据的有效性和安全性的技术。它涉及检查和过滤用户输入,以防止恶意代码注入和其他安全漏洞。


可以验证用户名只能包含数字和字母,限制用户对用户名的输入

package com.until; import java.util.regex.Pattern; /* * 字符串工具类 * */ public class StringUtil { /* * 判断用户名是否合法 * */ public static boolean isValidUsername(String username) { // 此处示例验证用户名只包含字母和数字 String regex = "^[a-zA-Z0-9]+$"; return Pattern.matches(regex, username); } }

在service层调用上述方法

202407211700874.png

测试

当我们再次输入在注册页面<script>alert(1)</script>,就无法注册成功

202407211659562.png

使用安全的html编码

安全的HTML编码涉及将特殊字符转换为对应的HTML实体。例如,将 < 转换为 < ,将 > 转换为 > ,将 " 转换为 ",将 & 转换为 & 等等。这样可以确保特殊字符在HTML中被正确解析,而不会被误认为是HTML标签或代码。


使用Apache Commons Text(Lang – Download Apache Commons LangCommons Text – 下载 Apache Commons Text)库中的 StringEscapeUtils.escapeHtml4 方法来进行安全的HTML编码。将用户输入的字符串进行编码后,特殊字符 <> 被转换为 <>

Registerservice层

通过使用StringEscapeUtils.escapeHtml4方法对用户输入的内容进行编码,将编码之后的内容进行数据库的插入

202407211811778.png

Userservice层

202407211814838.png

测试

当我们再次输入<script>alert(1)</script>后数据库中存储的就是编码之后的内容

202407211815188.png

202407211817990.png

再次登录之后发现就不会有弹窗了

202407211819046.png