模拟用户登录
用户名:fdsa
密码:fdsa’ or ‘1’='1
select * from t_user where login_name=‘fdsa’ and login_pwd=‘fdsa’ or ‘1’='1’登录成功。以上随意输入一个用户名和密码,登录成功了,这种现象被称为SQL注入现象!
导致SOL注人的根本原因是什么?怎么解决?
导致SOL注入的根本原因是:用户不是一般的用户,用户是懂得程序的,输入的用户名信息以及密码信息中含有SOL语句的关健字,这个SQL语句的关健字和底层的SQL语句进行 “字符串拼接”,导致原SQL语句的含义被扭曲了。最最最最最最主要的原因是:用户提供的信息参与了SQL语句的编译。
主要因素是:这个程序是先进行的字符串拼接,然后再进行的SQL语句的编译,正好被注入。
模拟SQL注入代码实战
public class 模拟SQL注入 {
public static void main(String[] args) {
//初始化一个界面,可以让用户输入用户名和密码
Map<String, String> map = initUI();
//连接数据库验证用户名和密码是否正确
boolean ok = checkNameAndPwd(map.get("a"),map.get("b"));
System.out.println(ok ? "登陆成功" : "登录失败");
}
/**
* 验证用户名和密码
* @param a 登录名
* @param b 登录密码
* @return true表示登陆成功 false表示登录失败
*/
private static boolean checkNameAndPwd(String a, String b) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
boolean ok = false; //默认登录失败的
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/feifei","root","297744");
//3.获取数据库操作对象
stmt = conn.createStatement();
//4.执行SQL语句
String sql = "select * from aa where name='"+ a +"' and mima = '"+b+"'";
System.out.println(sql);
rs = stmt.executeQuery(sql);//程序执行到此处,才会将sql语句发送到DBMS上,DBMS进行SQL语句编译
//处理查询结果集
//如果以上sql语句中用户和密码是正确的,那么结果集最多也就查询出一条记录,所以不需要while
if (rs.next()){
ok = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return ok;
}
/**
* 初始化界面,并接收用户的输入
* @return 存储登录和登录密码的Map集合
*/
private static Map<String,String> initUI() {
System.out.println("欢迎使用系统,请输入用户名和密码进行身份确认");
Scanner sc = new Scanner(System.in);
System.out.println("用户名:");
String a = sc.next();
System.out.println("密码:");
String b = sc.next();
//将用户名和密码放到map集合中
Map map = new HashMap();
map.put("a",a);
map.put("b",b);
//返回Map
return map;
}
}
那么可以看到,通过SQL注入的方法就可以实现登录,那么我们应该怎么避免注入呢? SQL注入的根本原因是:先进行了字符串的拼接,然后再进行的编译
java.sql.statement接口的特点:先进行字符串的拼接,然后再进行sql语句的编译。
优点:使用statement可以进行sql语句的拼接。
缺点:因为拼接的存在,导致可能给不法分子机会。导致SQL注入。
java.sql.PreparedStatement接口的特点:先进行SQL语句的编译,然后再进行sql语句的传值。
优点:避免SQL注入。
缺点:没有办法进行sql语句的拼接,只能给sql语句传值。
PreparedStatement预编译的数据库操作对象。
代码实战使用PreparedStatement解决sql注入。
public class 解决SQL注入问题 {
public static void main(String[] args) {
//初始化一个界面,可以让用户输入用户名和密码
Map<String, String> map = initUI();
//连接数据库验证用户名和密码是否正确
boolean ok = checkNameAndPwd(map.get("a"),map.get("b"));
System.out.println(ok ? "登陆成功" : "登录失败");
}
/**
* 验证用户名和密码
* @param a 登录名
* @param b 登录密码
* @return true表示登陆成功 false表示登录失败
*/
private static boolean checkNameAndPwd(String a, String b) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
boolean ok = false; //默认登录失败的
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/feifei","root","297744");
//3.获取预编译的数据库操作对象
//注意:一个问号?是一个占位符,一个占位符只能接受一个值/数据。占位符不能用单引号括起来
String sql = "select * from aa where name= ? and mima = ?";
stmt = conn.prepareStatement(sql);//此时发生sql给DBMS 进行sql语句的编译
//给占位符?传值
//jdbc中所有下标都是从1开始
//怎么解决SQL注入的:即使用户信息中有sql关键字,但是不参加编译就没事。
//stmt.setInt(1,444); 如果是setIng的话里面就要传入一个数字.setString是字符串
stmt.setString(1,a);//1代表第一个?
stmt.setString(2,b);//2代表第二个?
//4.执行SQL语句
//这个方法不能将SQL语句传进去,不能是这样rs = stmt.executeQuery(sql);
rs = stmt.executeQuery();
//5处理查询结果集
//如果以上sql语句中用户和密码是正确的,那么结果集最多也就查询出一条记录,所以不需要while
if (rs.next()){
ok = true;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return ok;
}
/**
* 初始化界面,并接收用户的输入
* @return 存储登录和登录密码的Map集合
*/
private static Map<String,String> initUI() {
System.out.println("欢迎使用系统,请输入用户名和密码进行身份确认");
Scanner sc = new Scanner(System.in);
System.out.println("用户名:");
String a = sc.next();
System.out.println("密码:");
String b = sc.next();
//将用户名和密码放到map集合中
Map map = new HashMap();
map.put("a",a);
map.put("b",b);
//返回Map
return map;
}
}