java代码审计--sql注入环境搭建


java里操作数据库的主要是MyBatis,Hibernate。接下来介绍一下MyBatis这个框架是怎么样造成SQL注入的吧。因为在网上也看了一些文章,发现基本上大家都是直接上框架,但是可能也有一些像我一样的小白对MyBatis和jdbc不太熟悉,所以,我打算从最基本的开始写,方便像我一样的小白入门吧。需要注意的是这里mybatis的配置,我是用的是xml的方式来配置,因为xml的逻辑性更好,明白xml配置的原理以后,使用注解就很容易了。

MyBatisda搭建

MyBatis是什么(MyBatis官网

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
建议大家有问题直接看官网,基本上都能够解决。这里我也是按照官网的步骤配置的。

导入依赖

首先是创建一个普通的maven项目,然后,为了一会方便演示sql注入的效果,我们可以在创建完成以后,添加一个web框架。
在这里插入图片描述
然后选择web框架,完成以后就像这样,会增加一个web文件夹。
在这里插入图片描述
接下来就是先配置MyBatis了,按照官网的步骤我们先导入配置文件,如下图所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SqlStudy</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

</project>

接下来在官网中介绍了使用MyBatis,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。并且官网给出了例子:
在这里插入图片描述
可以看到,例子是从mybatis-config.xml中构建了一个SqlSessionFactory。那么我们先来创建一个mybatis-config.xml文件。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--环境-->
    <environments default="development">
        <environment id="development">
            <!--事务管理-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>

</configuration>

这里官网也给出了一种不需要xml构建SqlSessionFactory的方法,但是个人感觉还是xml比较方便。所以这里就只采用这种xml的方法。这里需要注意的是在mappers标签中的信息,这里的resource是用来映射sql语句的,实际创建的时候,这里应该是写好Mapper层和对应的xml配置文件以后再配置的。
接下来跟着文档走,我们现在需要从SqlSessionFactory中获取一个SqlSession实例,因为SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。那么这里我们需要创建实体和Mapper接口了。

创建实体

实体可以理解成数据库里的表

package com.kento.pojo;

public class User &#123;
    private int id;
    private String name;
    private String pwd;

    public User(int id, String name, String pwd) &#123;
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    &#125;

    public User() &#123;
    &#125;

    public int getId() &#123;
        return id;
    &#125;

    public void setId(int id) &#123;
        this.id = id;
    &#125;

    public String getName() &#123;
        return name;
    &#125;

    public void setName(String name) &#123;
        this.name = name;
    &#125;

    public String getPwd() &#123;
        return pwd;
    &#125;

    public void setPwd(String pwd) &#123;
        this.pwd = pwd;
    &#125;

    @Override
    public String toString() &#123;
        return "User&#123;" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '&#125;';
    &#125;
&#125;

接着是Mapper,就是Dao层,如果有像我一样一开始不知道什么是Dao层,Controller层之类的,可以学一下javaweb,这里推荐B站狂神的视频。

创建Mapper接口

Mapper接口就是定义一些对数据库增删改查的函数。

package com.kento.Mapper;

import com.kento.pojo.User;

import java.util.List;

public interface UserMapper &#123;
    //获取全部用户
    List<User> getAllUser();
    //根据id获取用户
    User getUserById(int id);
    //根据用户名获取用户
    User getUserByName(String name);
    //添加用户
    void addUser(User user);
    //删除用户
    void deleteUser(int id);
    //修改用户
    void updateUser(User user);
&#125;

现在就是配置Mapper对应的xml了,因为我们需要将Mapper里对数据库的操作映射成sql语句。

<?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.kento.Mapper.UserMapper">
    <select id="getAllUser" resultType="com.kento.pojo.User">
        SELECT * FROM mybatis.user
    </select>
    <select id="getUserById" parameterType="int" resultType="com.kento.pojo.User">
        SELECT * FROM mybatis.user WHERE id = #&#123;id&#125; 
    </select>
    <select id="getUserByName" parameterType="String" resultType="com.kento.pojo.User">
        SELECT * FROM mybatis.user WHERE name = #&#123;name&#125; 
    </select>
    <insert id="addUser" parameterType="com.kento.pojo.User">
        INSERT INTO mybatis.user (id, name, pwd) VALUES (#&#123;id&#125;, #&#123;name&#125;, #&#123;pwd&#125;)
    </insert>
    <delete id="deleteUser" parameterType="int">
        DELETE FROM mybatis.user WHERE id = #&#123;id&#125;
    </delete>
    <update id="updateUser" parameterType="com.kento.pojo.User">
        UPDATE mybatis.user SET name = #&#123;name&#125;, pwd = #&#123;pwd&#125; WHERE id = #&#123;id&#125;
    </update>

</mapper>

到此为止我们的所有配置已经结束了,现在可以写一个test先试试了

package com.kento.Mapper;

import com.kento.pojo.User;
import com.kento.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UsermapperTest &#123;
    @Test
    public void testgetAllUsers() &#123;
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = usermapper.getAllUser();
        for (User user : allUser) &#123;

            System.out.println(user);
        &#125;
        sqlSession.close();
    &#125;

    @Test
    public void testgetUserById() &#123;
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
        User user = usermapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    &#125;

    @Test
    public void testgetUserByName() &#123;
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
        User user = usermapper.getUserByName("张三");
        System.out.println(user);
        sqlSession.close();
    &#125;
    @Test
    public void testdeleteUser()&#123;
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        sqlSession.getMapper(UserMapper.class).deleteUser(1);
        sqlSession.commit();
        sqlSession.close();
    &#125;
    @Test
    public void testaddUser()&#123;
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        User user = new User(1,"张三","123456");
        sqlSession.getMapper(UserMapper.class).addUser(user);
        sqlSession.commit();
        sqlSession.close();
    &#125;
&#125;

搭建中遇到的问题

资源文件不能加载

这里我遇到的问题是资源文件不能正常加载,这里我就不复现问题了,简单说一下解决问题的方法,就是maven中添加build指定资源文件的位置。

 <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

这里为了方便我把resources和java都进行了加载。

jdbc连接报错,显示连接失败

这里的原因比较多,网上有一些是因为连接超时的原因造成的,但是我是因为SSL连接的问题。我将useSSL设置成false就行了,但是建议大家把编码设为utf-8,方便显示中文。然后是建议使用mysql 5.x,因为新版的mysql还要设置时区。

<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>

hibernate使用

Servlet使用

Servlet是java用来做动态的一个东西,使用也是比较简单的,因为学习sql注入的时候,不需要理解很深,所以我就只说一下Servlet怎么配置,方便我们来从前端传入数据。然后,这里的设置就直接用注解来实现了,具体原理的话大家可以自己去学习一下,个人感觉通过xml来自己配置一下可以很容易的get到原理。

导入Servlet依赖

如果一开始大家不熟悉servlet的话可以直接导入以下的依赖,一般就够用了

 <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
        <!--JSTL表达式-->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/taglibs/standard -->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

Controller层代码

这里比较简单就直接放代码了

package com.kento.controller;

import com.kento.Mapper.UserMapper;
import com.kento.pojo.User;
import com.kento.utils.MyBatisUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

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;
import java.io.InputStream;
import java.io.PrintWriter;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

@WebServlet(name = "mybatis",urlPatterns = "/mybatissql")
public class MyBatisController extends HttpServlet &#123;
    SqlSession sqlSession;
    @Override
    public void init()&#123;
        sqlSession = MyBatisUtils.getSqlSession();
    &#125;
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException &#123;
        //显示内容
        resp.setContentType("text/html");
        resp.setCharacterEncoding("unicode");
        PrintWriter out = resp.getWriter();
//        try &#123;
//            String resource="mybatis-config.xml";
//            InputStream inputStream = Resources.getResourceAsStream(resource);
//            SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
//            SqlSession sqlSession = sqlSessionFactory.openSession();
//            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//            String name = req.getParameter("name");
//            User user = mapper.getUserByName(name);
//            sqlSession.close();
//            out.println(user.toString());
//        &#125;catch (Exception e)&#123;
//            e.printStackTrace();
//        &#125;finally &#123;
//            out.close();
//        &#125;

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        String name = req.getParameter("name");
        User user = mapper.getUserByName(name);
        out.println(user.toString());

    &#125;
    @Override
    public void destroy()&#123;
        sqlSession.close();
    &#125;


&#125;

然后这里我只有一个查询的接口,效果如下:
在这里插入图片描述


文章作者: kento
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kento !
评论
  目录