一个简单的登陆案例模拟sql注入及Statement与PreparedStatement对比

1. sql注入

由于后台的sql是拼接而来,其中的参数是用户提交的,如果用户在提交参数时,参杂了一些sql的关键字或者特殊符号,可能会导致sql语义的改变,从而造成一些意外的操作。

2.Statement

  • Statement主要用于执行静态sql语句,即内容固定不变的sql语句
  • Statement没执行一次都要对传入的sql语句编译一次,效率较差
  • 某些情况下,sql语句只是其中的参数有所不同,其余子句完全相同,不适用于Statement
  • 会被sql注入攻击

模拟sql 注入的代码实现如下:

公共类JdbcUtils 代码(提供资源关闭和获取连接方法):

import java.sql.*;
 
/**
 * jdbc 工具类
 *
 * @author ww
 * @date 2020/6/12 14:26
 */
public class JdbcUtils {
 
    /**
     * 资源关闭方法
     *
     * @param rs
     * @param connection
     * @param statement
     */
    public static void close(ResultSet rs,
                             Connection connection,
                             Statement statement) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                rs = null;
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                statement = null;
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                connection = null;
            }
        }
 
    }
 
    /**
     * 获取连接方法
     * @return
     */
    public static Connection getConnection() {
        {
            //1.注册数据库驱动
            try {
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接
                Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
                return connection;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
 
        }
 
    }
}

在数据库建立一个数据表,并插入数据:

当正确输入用户名密码时,显示登陆成功

当密码输入错误时,提示用户名或密码错误

下面验证sql 注入:

用户名输入:张三'# 时:显示登陆成功

分析:用户名和密码都不正确,结果登陆成功。查看拼接后的sql,因为用户名后面的# 在sql中是属于注释的符号,所以用户名后面的代码相当于被注释掉了,所以能登陆成功

用户名为:张三' or '1=1 时:显示登陆成功

分析:当用户名和密码都不正确,结果登陆成功。查看拼接后的sql,因为后面的语句为 or 1=1,因为1=1 为true,所以该语句执行成功,最终显示登陆成功

代码如下:


import cn.tedu.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class Login {
    public static void main(String[] args) {
        //准备用户表
        //create table user(id int,username varchar(20),password varchar(20));
        //insert into user values(1,'张三','123');
        //insert into user values(2,'李四','123');

        //String username = "张三";
        //String username = "张三'#";
        String username = "张三' or '1=1";
        String password = "1234";


        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            stat = conn.createStatement();
            //查询用户名密码是否存在
            String sql = "select * from user where " +
                    "name='"+username+"' and password='"+password+"'";
            System.out.println("拼接后的sql:"+sql);
            rs = stat.executeQuery(sql);
            if(rs.next()){//查到了
                System.out.println("欢迎登陆");
            }else {//用户名密码不存在
                System.out.println("用户名或密码错误");
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            JDBCUtils.close(conn,stat,rs);
        }
    }
}

3.PreparedStatement

  • PreparedStatement是接口,继承自Statement
  • 某些情况下,sql语句只是其中的参数有所不同,其余子句完全相同,适用于PreparedStatement
  • 预防sql注入攻击
  • SQL语句提前编译,三种常用方法execute,executeQuery,executeUpdate 已被更改,不在需要参数

PreparedStatement原理:

  • PreparedStatement实例包含已事先编译的sql语句
  • SQL语句可有一个或多个IN参数(IN参数的值在sql语句创建时未被指定,语句为没过IN参数保留一个问号(“?”)作为占位符,每个问号必须在该语句执行前,通过适当的SetInt 或者SetString方法提供)
  • 由于PreparedStatement对象已预编译过,所以其执行速度要快于Statement对象。由此,多次执行sql语句经常创建为PreparedStatement对象,以提高效率
  • 批处理

此时分别验证上面2种sql注入情况,均不通过

 

只有正确输入用户名密码才能验证通过

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值