😍
信息安全笔记
  • 序言
  • 引用
  • devsecops
    • checkmarx
    • codeql
    • coverity
    • fortifiy
    • sca
      • Dependency Check
      • Dependency Track
    • 案例
      • SCA在得物DevSecOps平台上应用
    • 漏洞修复
      • Hello Java Sec
  • 书籍
    • 编译原理
      • 第一章 引论
  • 代码审计
    • JAVA漏洞
      • CRLF注入
      • Java RMI
      • JSONP
      • JWT
      • Log4j2
      • SPEL
      • SQL注入
      • SSRF
      • SSTI
      • XSS
      • XStream
      • XXE
      • 反序列化
      • 命令执行
      • 文件操作
    • 准备
      • 远程调试
      • 配置IDEA
  • 安全测试
    • APP渗透
      • 安卓5版本抓包
      • 安卓7版本抓包
      • 安卓9版本抓包
    • Linux提权
    • WEB应用
    • Windows提权
    • 信息收集
    • 免杀技巧
    • 其他
      • 反弹shell总结
    • 前端绕过
    • 后渗透
    • 容器渗透
    • 攻击绕过
    • 木马病毒
    • 横向移动
      • AS-REP Roasting攻击
      • Kerberoasting 攻击
    • 缓冲区溢出
  • 安全漏洞
    • Linux提权漏洞
    • Linux漏洞
    • Windows提权漏洞
    • Windows漏洞
    • 应用漏洞
    • 未授权漏洞
      • ActiveMQ未授权访问漏洞
      • Apache Flink未授权访问漏洞
      • Atlassian Crowd 未授权访问漏洞
      • clickhouse 未授权访问漏洞
      • CouchDB未授权访问漏洞
      • Docker未授权访问漏洞
      • druid 监控页未授权访问漏洞
      • Dubbo 未授权访问漏洞
      • Hadoop YARN resourcemanager 未授权访问漏洞
      • Hadoop Yarn RPC未授权访问漏洞
      • InfluxDB API 未授权访问漏洞
      • JBoss未授权访问漏洞
      • Jenkins未授权访问漏洞
      • Jupyter Notebook 未授权访问漏洞
      • Kafka Manager 未授权访问漏洞
      • Kibana 未授权访问漏洞
      • Kong未授权访问漏洞
      • Kubernetes Api Server 未授权访问
      • LDAP未授权访问漏洞
      • Memcached未授权访问漏洞
      • MongoDB未授权访问漏洞
      • NFS未授权访问漏洞
      • RabbitMQ 未授权访问漏洞
      • Redis未授权访问漏洞
      • Rsync未授权访问漏洞
      • Spark 未授权访问漏洞
      • Spring Cloud Gateway Server 未授权访问漏洞
      • SpringBoot Actuator未授权访问漏洞
      • VNC Server 未授权访问漏洞
      • Weblogic 未授权访问漏洞
      • Zabbix未授权访问漏洞
      • ZooKeeper未授权访问漏洞
  • 安全证书
    • CISSP
    • CRTO
      • 考证经验分享
    • OSCP
      • 考证经验分享
  • 社会工程学
    • 网络钓鱼
  • 运维配置
    • Kubernetes
      • 安装部署
  • 靶场环境
    • attackdefense
    • HTB
    • tryhackme
    • vulnhub
      • ACID RELOADED
      • ACID SERVER
      • Assertion101
      • BBSCute 1.0.2
      • BILLY MADISON 1.1
      • Bob 1.0.1
      • Born2Root 2
      • Born2Root:1
      • BossPlayersCTF
      • Bottleneck
      • Brainpan 1
      • Breach 1
      • Breach 2.1
      • Breach 3.0.1
      • BSides Vancouver 2018
      • BTRSys2.1
      • Covfefe
      • CYBERSPLOIT 1
      • Darknet:1.0
      • Dawn
      • Dawn2
      • Dawn3
      • DC 1
      • DC 2
      • DC 3.2
      • DC 4
      • DC 6
      • DC 8
      • DC 5
      • DC 7
      • DC 9
      • Deception
      • DEFCON Toronto Galahad
      • DERPNSTINK 1
      • DevGuru 1
      • DEVRANDOM SLEEPY
      • digitalworld.local BRAVERY
      • digitalworld.local DEVELOPMENT
      • digitalworld.local FALL
      • digitalworld.local JOY
      • digitalworld.local MERCY v2
      • digitalworld.local snakeoil
      • digitalworld.local TORMENT
      • DJINN 1
      • Djinn3
      • Election 1
      • Escalate Linux:1
      • EVM 1
      • Five86.2
      • FristiLeaks:1.3
      • Funbox
      • FunboxEasy
      • FunboxEasyEnum
      • FunboxRookie
      • Gaara
      • Geisha
      • Gitroot
      • Glasglow 1.1
      • GoldenEye 1
      • GREENOPTIC 1
      • Ha-natraj
      • Hack Me Please
      • Hacker kid 1.0.1
      • HackLAB:vulnix
      • HACKME 1
      • HACKME 2
      • HA:WORDY
      • Healthcare 1
      • IMF
      • Inclusiveness
      • Infosec Prep OSCP Box
      • InsanityHosting
      • Katana
      • Kioptrix Level 1.1
      • Kioptrix Level 1
      • Kioptrix 2014
      • Kioptrix Level 1.2
      • Kioptrix Level 1.3
      • Kvasir
      • Lampiao
      • LazySysAdmin
      • LemonSqueezy
      • Lin.Security
      • Loly
      • Lord of the Root 1.0.1
      • Metasploitable 3
      • Monitoring
      • MORIA 1.1
      • Mr-Robot:1
      • My-CMSMS
      • Node 1
      • NoName
      • NullByte
      • OZ
      • Photographer 1
      • Pinkys Palace v1
      • Pinkys Palace v2
      • Pinkys Palace v3
      • Pinkys Palace v4
      • Potato
      • Powergrid
      • Prime 1
      • Pwned1
      • PwnLab:init
      • PWNOS:1.0
      • PWNOS:2.0
      • PyExp
      • Raven 1
      • Raven 2
      • Readme 1
      • RICKDICULOUSLYEASY 1
      • Sar:1
      • Sedna
      • Seppuku
      • SickOs 1.2
      • Simple
      • Sky Tower
      • SolidState
      • Solstice
      • SoSimple
      • Spydersec
      • Stapler 1
      • Sumo
      • SUNSET MIDNIGHT
      • SunsetMidnight
      • SunsetNoontide
      • Sunset:Decoy
      • Ted
      • Temple of Doom
      • Tiki-1
      • TOMMY BOY 1
      • Toppo 1
      • TRE 1
      • Troll 1
      • Troll 2
      • Troll 3
      • Vegeta1
      • Violator
      • Vulnerable Docker 1
      • VulnOS 2
      • W34kn3ss 1
      • Wallaby's Nightmare
      • Web Developer 1
      • Wintermute
      • Wpwn
      • xxe
      • Y0usef
      • ZICO2:1
    • 云原生
      • kubernetes-goat
    • 域环境
      • PowerShell 搭建AD域渗透环境
    • 红日靶场
由 GitBook 提供支持
在本页
  • jdbc
  • 修复代码(预编译)
  • mybatis+springboot
  • 修复代码

这有帮助吗?

  1. 代码审计
  2. JAVA漏洞

SQL注入

jdbc

需要将mysql-connector-java-8.0.28.jar放到tomcat的lib目录下面

SQL代码

CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(50) NOT NULL
);

INSERT INTO users (id, name)
VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');

漏洞代码

<%@page import="java.sql.*"%>
<%
    String name = request.getParameter("name");
    String query = "SELECT * FROM users WHERE name = '" + name + "'";

    // 设置数据库连接
    String url = "jdbc:mysql://192.168.32.130:3306/test";
    String username = "root";
    String password = "123456";
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection(url, username, password);

    // 执行查询
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(query);

    // 显示查询结果
    out.println("<table>");
    out.println("<tr><th>ID</th><th>Name</th></tr>");
    while (rs.next()) {
        int id = rs.getInt("id");
        String uname = rs.getString("name");
        out.println("<tr><td>" + id + "</td><td>" + uname + "</td></tr>");
    }
    out.println("</table>");

    // 关闭数据库连接
    rs.close();
    stmt.close();
    conn.close();
%>

修复代码(预编译)

  String query = "SELECT * FROM users WHERE name = ?";

  // 执行查询
  PreparedStatement stmt = conn.prepareStatement(query);
  stmt.setString(1, name);
  ResultSet rs = stmt.executeQuery();

预编译语句是一种更安全和更高效的处理方式,它无法处理某些情况,例如like和order by子句中的动态值。

为了解决这些问题,有几种可行的方法:

  1. 在使用字符串拼接构建SQL语句时,必须对动态值进行适当的转义和验证,以避免SQL注入攻击。

例如,在使用like子句时,可以这样处理:

String username = request.getParameter("username");
String sql = "SELECT * FROM users WHERE username LIKE ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, "%" + username.replace("%", "\\%").replace("_", "\\_") + "%");
ResultSet rs = stmt.executeQuery();
  1. 对于无法使用预编译的查询,可以在编写代码时尽量将动态值分离出来,使其成为固定的字符串。

例如,在使用order by子句时,可以这样处理:

String sort = req.getParameter("sort");
String sql = "SELECT * FROM users ORDER BY " + validateSort(sort);
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();

private String validateSort(String sort) {
  // 对sort参数进行验证和转义,确保只包含允许的字段名和排序方式(ASC或DESC),以避免SQL注入攻击
  if (sort == null) {
    return "id ASC"; // 默认排序方式
  }
  String[] parts = sort.split(" ");
  if (parts.length != 2 || (!"ASC".equals(parts[1]) && !"DESC".equals(parts[1]))) {
    return "id ASC"; // 非法排序参数,使用默认排序方式
  }
  String field = parts[0];
  // 对field进行验证和转义,确保只包含允许的字段名
  if (!"id".equals(field) && !"name".equals(field) && !"age".equals(field)) {
    return "id ASC"; // 非法排序字段,使用默认排序方式
  }
  return field + " " + parts[1];
}

mybatis+springboot

使用MyBatis和Spring Boot的示例代码的典型目录结构

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           ├── controller
│   │   │           │   └── UserController.java
│   │   │           ├── mapper
│   │   │           │   └── UserMapper.java
│   │   │           ├── model
│   │   │           │   └── User.java
│   │   │           ├── service
│   │   │           │   └── UserService.java
│   │   │           └── MyApplication.java
│   │   └── resources
│   │       ├── mapper
│   │       │   └── UserMapper.xml
│   │       └── application.properties
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── service
│                       └── UserServiceTest.java
└── pom.xml

创建一个maven项目,名字是example。

package com.example.controller;

import com.example.mapper.UserMapper;
import com.example.model.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private UserMapper userMapper;
    // http://127.0.0.1:8080/users/1/
    @GetMapping(value = "/users/{id}", produces = "application/json")
    public ResponseEntity<User> getUser(@PathVariable String id) {
        User user = userService.findById(id);
        return ResponseEntity.ok(user);
    }

    // http://127.0.0.1:8080/users/ids/?ids=1,2,3
    @GetMapping("/users/ids")
    public List<User> findUsersByIds(@RequestParam String ids) {
        List<User> users = userMapper.findUsersByIds(ids);
        return users;
    }
    // http://127.0.0.1:8080/users/name?name=A
    @GetMapping("/users/name")
    public List<User> findUsersByNameLike(@RequestParam String name) {
        List<User> users = userMapper.findUsersByNameLike(name);
        return users;
    }
    // http://127.0.0.1:8080/users/sort?orderByColumn=name&orderByDirection=asc
    @GetMapping("/users/sort")
    public List<User> findUsersOrderBy(@RequestParam String orderByColumn, @RequestParam String orderByDirection) {
        List<User> users = userMapper.findUsersOrderBy(orderByColumn, orderByDirection);
        return users;
    }
    // http://127.0.0.1:8080/users/names?names=Alice&names=Bob
    @GetMapping("/users/names")
    public List<User> findUsersByNames(@RequestParam List<String> names) {
        List<User> users = userMapper.findUsersByNames(names);
        return users;
    }
    
}
package com.example.mapper;

import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper {
    User findById(@Param("id") String id);

    List<User> findUsersByIds(@Param("ids") String ids);

    List<User> findUsersByNameLike(@Param("name") String name);

    List<User> findUsersOrderBy(@Param("orderByColumn") String orderByColumn, @Param("orderByDirection") String orderByDirection);

    List<User> findUsersByNames(@Param("names") List<String> names);
}
package com.example.model;

public class User {
    private Long id;
    private String name;
    private Integer age;

    public User() {
        // empty constructor required by some frameworks
    }

    public User(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
package com.example.service;

import com.example.mapper.UserMapper;
import com.example.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User findById(String id) {
        return userMapper.findById(id);
    }

}
package com.example;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mapper")
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.mapper.UserMapper">

    <select id="findById" resultType="User">
        SELECT * FROM users WHERE id = '${id}'
    </select>

    <select id="findUsersByIds" resultType="User">
        SELECT * FROM users
        WHERE id IN (${ids})
    </select>

    <select id="findUsersByNameLike" resultType="User">
        SELECT * FROM users
        WHERE name LIKE '%${name}%'
    </select>

    <select id="findUsersOrderBy" resultType="User">
        SELECT * FROM users
        ORDER BY ${orderByColumn} ${orderByDirection}
    </select>

    <select id="findUsersByNames" resultType="User">
        SELECT * FROM users
        WHERE name IN
        <foreach item="name" collection="names" open="(" separator="," close=")">
            '${name}'
        </foreach>

    </select>

</mapper>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.32.130:3306/test?characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456

mybatis.type-aliases-package=com.example.model
mybatis.mapper-locations=classpath:mapper/*.xml

修复代码

在 MyBatis 中,你可以使用占位符(?)来表示参数,这样就可以将参数值与 SQL 语句分离。在 mapper XML 文件中,你可以使用 <select>、<insert> 或 <update> 标签来定义 SQL 语句,然后使用 #{} 占位符来引用参数。例如:

<select id="getUserById" parameterType="Long" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>

当你使用参数化查询时,MyBatis 会自动将 SQL 语句转换为预编译语句,从而避免 SQL 注入攻击。预编译语句将会在 SQL 执行之前进行编译和解析,因此恶意注入的代码将无法被执行。

在 mapper XML 文件中,你可以使用 #{} 占位符来引用参数。例如:

<select id="findUsersByNames" resultType="User">
    SELECT * FROM users
    WHERE name IN
    <foreach item="name" collection="names" open="(" separator="," close=")">
        #{name}
    </foreach>

</select>

避免在 SQL 语句中拼接参数值,因为这样容易受到 SQL 注入攻击。如果你需要在 SQL 语句中拼接字符串或其他类型的值,请使用 MyBatis 内置的字符串函数或者使用占位符来引用参数。

<select id="getUserByName" parameterType="String" resultType="User">
    SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')
</select>

在 mapper XML 文件中,你可以使用 <select>、<insert> 或 <update> 标签来定义 SQL 语句,然后使用 #{} 占位符来引用参数。例如:

<select id="findUsers" parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE name LIKE #{name}
    ORDER BY #{orderByColumn} #{orderByDirection}
</select>

在这个例子中,findUsers 方法将会接收一个 Map 类型的参数,并返回匹配的 User 对象列表。#{orderByColumn} 和 #{orderByDirection} 占位符将会将参数值安全地插入到 SQL 语句中,并避免了 SQL 注入攻击。

为了避免 SQL 注入攻击,你应该限制 ORDER BY 子句中的列名只能为预定义的值。你可以在代码中定义一个白名单,只允许一些特定的列名。例如:

package com.example.service;

import com.example.mapper.UserMapper;
import com.example.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    private static final List<String> ALLOWED_COLUMNS = Arrays.asList("id", "name", "age");
    private static final List<String> ALLOWED_DIRECTIONS = Arrays.asList("asc", "desc");


    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public List<User> findUsersOrderBy(String orderByColumn, String orderByDirection) {
        if (!ALLOWED_COLUMNS.contains(orderByColumn)) {
            throw new IllegalArgumentException("Invalid column name: " + orderByColumn);
        }
        if (!ALLOWED_DIRECTIONS.contains(orderByDirection)) {
            throw new IllegalArgumentException("Invalid direction: " + orderByDirection);
        }
        return userMapper.findUsersOrderBy(orderByColumn, orderByDirection, ALLOWED_COLUMNS, ALLOWED_DIRECTIONS);
    }


}
package com.example.mapper;

import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper {

    List<User> findUsersOrderBy(
            @Param("orderByColumn") String orderByColumn,
            @Param("orderByDirection") String orderByDirection,
            @Param("allowedColumns") List<String> allowedColumns,
            @Param("allowedDirections") List<String> allowedDirections);

}
<select id="findUsersOrderBy" resultType="User">
    SELECT * FROM users
    <if test="allowedColumns.contains(orderByColumn) and allowedDirections.contains(orderByDirection)">
        ORDER BY ${orderByColumn} ${orderByDirection}
    </if>
</select>
    @GetMapping("/users/sort")
    public List<User> findUsersOrderBy(
            @RequestParam String orderByColumn,
            @RequestParam(defaultValue = "ASC") String orderByDirection) {
        return userService.findUsersOrderBy(orderByColumn, orderByDirection);
    }
上一页SPEL下一页SSRF

最后更新于2年前

这有帮助吗?

image-20230306160608892
image-20230306165339713