SpringBoot审计基础知识
字数 1530 2025-08-24 07:48:33

SpringBoot安全审计基础知识

1. SQL注入漏洞

1.1 JDBC注入

public String int_based(String id, Model model) {
    StringBuilder result = new StringBuilder();
    try {
        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
        String sql = "select * from users where id = " + id;  // 直接拼接SQL导致注入
        result.append("执行SQL语句: ").append(sql).append(System.lineSeparator());
        PreparedStatement st = conn.prepareStatement(sql);  // 错误使用预编译
        ResultSet rs = st.executeQuery();
        // ...结果处理...
    } catch (Exception e) {
        model.addAttribute("results", e.toString());
    }
    return "basevul/sqli/jdbc_int_based";
}

修复方案

  • 使用参数化查询:String sql = "select * from users where id = ?";
  • 正确使用PreparedStatement:st.setInt(1, Integer.parseInt(id));

1.2 MyBatis注入

1.2.1 Like查询注入

<!-- 错误写法 -->
Select * from users where username like '%#{username}%'  // 会报错
Select * from users where username like '%${username}%'  // 导致注入

<!-- 正确写法 -->
Select * from users where username like concat('%',#{username}, '%')

POC: xxx%' union select database(),user(),@@version,4,5 -- -

1.2.2 Order By注入

<!-- 错误写法 -->
Select * from users order by ${field}  // 导致注入

<!-- 修复方案 -->
- 避免使用${},改为白名单校验
- 在Java代码中处理排序逻辑

POC: id and (updatexml(1,concat(0x7e,(select user())),0))-- -

1.2.3 In查询注入

<!-- 错误写法 -->
Select * from users where id in (${ids})  // 导致注入

<!-- 修复方案 -->
- 使用foreach标签
- 在Java代码中处理参数校验

POC: 1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- -

2. 鉴权类漏洞

2.1 未授权访问

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/", "/error", "/login", "/index", "/captcha", 
                               "/admin/login", "/admin/logout", "/api/*", 
                               "/css/*", "/images/*", "/upload/*", "/js/*", 
                               "/lib/*", "/page/*", "/unauth/*", "/shiro", 
                               "/home/shiro/*");  // 排除路径需谨慎
    }
}

风险点

  • 排除路径过多可能导致未授权访问
  • 静态资源目录配置不当

2.2 登录验证问题

public String login(String username, String password, String captcha, 
                   String path, HttpSession session, HttpServletRequest request, 
                   Model model) {
    if (request.getMethod().equals("GET")) return "login";
    if (!CaptchaUtil.ver(captcha, request)) {
        CaptchaUtil.clear(request);
        model.addAttribute("msg", "验证码不正确");
        return "login";
    }
    Admin admin = adminService.login(username, password);  // 调用接口实现
    if (admin != null) {
        session.setAttribute("username", username);
        if (path != null) {
            return "redirect:" + path;  // 重定向路径需校验
        }
        return "redirect:/home";
    }
    // ...
}

风险点

  • 重定向路径未校验可能导致开放重定向
  • 密码传输和存储安全性
  • 会话固定风险

3. 文件操作漏洞

3.1 文件上传漏洞

@RequestMapping("/upload")
public String upload(MultipartFile file, Model model) throws IOException {
    String fileName = file.getOriginalFilename();  // 未校验文件名
    FileOutputStream fos = new FileOutputStream(new File("/tmp/" + fileName));
    file.transferTo(fos);  // 直接保存文件
    fos.close();
    model.addAttribute("fileName", fileName);
    return "upload";
}

3.1.1 安全防护措施

后缀白名单

String Suffix = fileName.substring(fileName.lastIndexOf("."));
String[] SuffixSafe = {".jpg", ".png", ".jpeg", ".gif", ".bmp", ".ico"};

文件类型白名单

private static final String[] ALLOWED_IMAGE_TYPES = {
    "image/jpeg", "image/png", "image/gif", "image/bmp"
};

public static boolean isAllowed(File file) throws IOException {
    String contentType = getContentType(file);
    return Arrays.asList(ALLOWED_IMAGE_TYPES).contains(contentType);
}

3.2 文件下载漏洞

public static boolean checkTraversal(String content) {
    return content.contains("..") || content.contains("/");  // 目录穿越检查
}

// 使用示例
Security.checkTraversal(filename);

风险点

  • 目录穿越攻击
  • 文件类型检查不足
  • 文件权限设置不当

4. 命令执行/代码执行漏洞

4.1 Runtime.exec命令执行

public String RuntimeExec(String cmd, Model model) {
    StringBuilder sb = new StringBuilder();
    String line;
    try {
        Process proc = Runtime.getRuntime().exec(cmd);  // 直接执行用户输入
        // ...结果处理...
    } catch (IOException e) {
        sb.append(e);
    }
    model.addAttribute("results", sb.toString());
    return "basevul/rce/runtime";
}

防护措施

  • 避免直接执行用户输入
  • 使用白名单限制可执行命令
  • 对输入进行严格过滤

4.2 ScriptEngineManager代码执行

public String loadJsExec(String url, Model model) {
    try {
        ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
        Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
        String payload = String.format("load('%s')", url);  // 加载远程脚本
        engine.eval(payload, bindings);  // 执行脚本
        // ...
    }
}

POC:

var a = mainOutput();
function mainOutput() {
    var x=java.lang.Runtime.getRuntime().exec("calc");
}

绕过技巧:

var x=java.lang./**/Runtime.getRuntime().exec("calc");

4.3 Groovy代码执行

public String groovyExec(String cmd, Model model) {
    GroovyShell shell = new GroovyShell();
    try {
        shell.evaluate(cmd);  // 直接执行Groovy代码
        model.addAttribute("results", "执行成功!!!");
    } catch (Exception e) {
        model.addAttribute("results", e.toString());
    }
    return "basevul/rce/groovy";
}

POC:

  • Windows: "calc".execute()
  • macOS: "open -a Calculator".execute()

4.4 ProcessBuilder命令执行

public String ProcessBuilderExec(String ip, String safe, Model model) {
    if (safe != null) {
        if (Security.checkOs(ip)) {  // 命令过滤
            model.addAttribute("results", "检测到非法命令注入");
            return "basevul/rce/processbuilder";
        }
    }
    String[] cmdList = {"cmd", "/c", "ping -n 1 " + ip};  // 命令拼接
    // ...执行过程...
}

过滤方法:

public static boolean checkOs(String content) {
    String black = "|&;$";
    String[] black_list = black.split(",");
    for (String s : black_list) {
        if (content.contains(s)) {
            return true;
        }
    }
    return false;
}

4.5 ProcessImpl反射调用

public String processImplExec(String cmd, Model model) {
    try {
        Class<?> clazz = Class.forName("java.lang.ProcessImpl");
        Method method = clazz.getDeclaredMethod("start", String[].class, 
            Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
        method.setAccessible(true);
        Process e = (Process) method.invoke(null, new String[]{cmd}, null, 
            null, null, false);  // 反射调用ProcessImpl
        // ...结果处理...
    }
}

风险点

  • 通过反射绕过安全限制
  • 可用于绕过某些防护措施

5. SSRF漏洞

5.1 URLConnection SSRF

public String urlConnection(@RequestParam String url, String isHttp, String isIntranet) {
    if (url.equals("")) return "请输入url";
    if (isHttp != null && isHttp.equals("true")) {
        if (!Security.isHttp(url)) {  // 协议限制
            return "不允许非http/https协议!!!";
        }
    }
    if (isIntranet != null && isIntranet.equals("true")) {
        if (Security.isIntranet(url)) {  // 内网限制
            return "不允许访问内网!!!";
        }
    }
    String results = HTTP.URLConnection(url);  // 发起请求
    return results;
}

防护方法:

public static boolean isHttp(String url) {
    return url.startsWith("http://") || url.startsWith("https://");
}

public static boolean isIntranet(String url) {
    Pattern reg = Pattern.compile("^(127\\.0\\.0\\.1)|(localhost)|(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|(192\\.168\\.\\d{1,3}\\.\\d{1,3})$");
    Matcher match = reg.matcher(url);
    return match.find();
}

支持的协议:

  • http、https、file、ftp、mailto、jar、netdoc
  • gopher(JDK8以后被移除)

利用示例:

  • file:///C:\windows\win.ini
  • http://internal-server/secret

6. SpEL表达式注入

public String spelVul(String exp, Model model) {
    try {
        ExpressionParser parser = new SpelExpressionParser();
        // StandardEvaluationContext权限过大
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        String result = parser.parseExpression(exp).getValue(evaluationContext).toString();
        model.addAttribute("results", result);
    } catch (ParseException e) {
        model.addAttribute("results", e.toString());
    }
    return "basevul/spel/spel";
}

POC: T(java.lang.Runtime).getRuntime().exec("calc.exe")

修复方案:

  • 使用SimpleEvaluationContext替代StandardEvaluationContext
  • 对输入表达式进行过滤

7. SSTI模板注入

7.1 Thymeleaf注入

漏洞代码:

@RequestMapping("/thymeleaf")
public String thymeleaf(String content) {
    return "user/" + content + "/welcome";  // 模板路径被污染
}

POC:

GET /home/ssti/noreturn/__$%7BT(java.lang.Runtime).getRuntime().exec(%22calc%22)%7D__::.x HTTP/1.1

无返回值控制器:

@RequestMapping("/noreturn/{content}")
public void noReturn(String content) {
    System.out.println("ok");
}

风险点:

  • 当控制器无返回值时,Spring会使用请求URL作为视图名称
  • 攻击者可以控制模板名称触发RCE

8. XXE漏洞

8.1 XMLReader

public String XMLReader(@RequestBody String content, String entity) {
    try {
        if (entity != null && entity.equals("true") && Security.checkXXE(content)) {
            return "检测到XXE攻击";
        }
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        // 未禁用DOCTYPE
        // xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        xmlReader.parse(new InputSource(new StringReader(content)));
        return "XMLReader 解析成功";
    } catch (Exception e) {
        return e.toString();
    }
}

8.2 SAXReader

public String SAXReader(@RequestBody String content, String entity) {
    try {
        SAXReader sax = new SAXReader();
        // 未禁用外部实体
        // sax.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        sax.read(new InputSource(new StringReader(content)));
        return "SAXReader 解析成功";
    } catch (Exception e) {
        return e.toString();
    }
}

8.3 DocumentBuilder

public String DocumentBuilder(@RequestBody String content, String entity) {
    try {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 未设置安全属性
        // dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(new InputSource(new StringReader(content)));
        // ...
    }
}

8.4 Unmarshaller

public String Unmarshaller(@RequestBody String content, String entity) {
    try {
        JAXBContext context = JAXBContext.newInstance(Student.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        XMLInputFactory xif = XMLInputFactory.newFactory();
        // 默认情况下在1.8版本上不能加载外部dtd文件
        // xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
        XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(content));
        Object o = unmarshaller.unmarshal(xsr);
        return o.toString();
    }
}

8.5 XXE防护方法

public static boolean checkXXE(String content) {
    String black = "ENTITY";
    return content.toUpperCase().contains(black);
}

XXE利用Payload:

<!-- Windows -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///C:\windows\win.ini">
]>
<person><name>&xxe;</name></person>

<!-- Linux -->
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<person><name>&xxe;</name></person>

修复方案:

  • 禁用DTD: setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)
  • 禁用外部实体
  • 使用安全的XML解析器配置
SpringBoot安全审计基础知识 1. SQL注入漏洞 1.1 JDBC注入 修复方案 : 使用参数化查询: String sql = "select * from users where id = ?"; 正确使用PreparedStatement: st.setInt(1, Integer.parseInt(id)); 1.2 MyBatis注入 1.2.1 Like查询注入 POC : xxx%' union select database(),user(),@@version,4,5 -- - 1.2.2 Order By注入 POC : id and (updatexml(1,concat(0x7e,(select user())),0))-- - 1.2.3 In查询注入 POC : 1,2,3) and (updatexml(1,concat(0x7e,(select user())),0))-- - 2. 鉴权类漏洞 2.1 未授权访问 风险点 : 排除路径过多可能导致未授权访问 静态资源目录配置不当 2.2 登录验证问题 风险点 : 重定向路径未校验可能导致开放重定向 密码传输和存储安全性 会话固定风险 3. 文件操作漏洞 3.1 文件上传漏洞 3.1.1 安全防护措施 后缀白名单 : 文件类型白名单 : 3.2 文件下载漏洞 风险点 : 目录穿越攻击 文件类型检查不足 文件权限设置不当 4. 命令执行/代码执行漏洞 4.1 Runtime.exec命令执行 防护措施 : 避免直接执行用户输入 使用白名单限制可执行命令 对输入进行严格过滤 4.2 ScriptEngineManager代码执行 POC : 绕过技巧 : 4.3 Groovy代码执行 POC : Windows: "calc".execute() macOS: "open -a Calculator".execute() 4.4 ProcessBuilder命令执行 过滤方法 : 4.5 ProcessImpl反射调用 风险点 : 通过反射绕过安全限制 可用于绕过某些防护措施 5. SSRF漏洞 5.1 URLConnection SSRF 防护方法 : 支持的协议 : http、https、file、ftp、mailto、jar、netdoc gopher(JDK8以后被移除) 利用示例 : file:///C:\windows\win.ini http://internal-server/secret 6. SpEL表达式注入 POC : T(java.lang.Runtime).getRuntime().exec("calc.exe") 修复方案 : 使用 SimpleEvaluationContext 替代 StandardEvaluationContext 对输入表达式进行过滤 7. SSTI模板注入 7.1 Thymeleaf注入 漏洞代码 : POC : 无返回值控制器 : 风险点 : 当控制器无返回值时,Spring会使用请求URL作为视图名称 攻击者可以控制模板名称触发RCE 8. XXE漏洞 8.1 XMLReader 8.2 SAXReader 8.3 DocumentBuilder 8.4 Unmarshaller 8.5 XXE防护方法 XXE利用Payload : 修复方案 : 禁用DTD: setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) 禁用外部实体 使用安全的XML解析器配置