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注入情况,均不通过
只有正确输入用户名密码才能验证通过