1、SQL注入原理
1.1 SQL注入攻击:
上一篇文章所使用到的SQL语句是拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入的数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,从而跳过程序处理的业务逻辑,这样的攻击方式就叫做sql注入攻击
1.2 SQL注入案例
数据库中的数据
mysql> select * from t_user;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | 123123 |
+----+----------+----------+
1 row in set (0.00 sec)
实现从数据库中拿取账户名和密码进行登录
package com.company.jdbc;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
// 实现从数据库中拿取数据,并模拟实现登录
// 但是没有解决sql注入问题
public class JDBCLoginTest {
public static void main(String[] args) throws IOException {
// 接收用户登录是输入的账号和密码
System.out.println("********************欢迎来到登录界面**********************");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("输入账号:");
String username = br.readLine();
System.out.print("输入密码:");
String passwd = br.readLine();
// ---------------------连接数据库-------------------------
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
boolean flag = false;
try {
// 注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、 获取数据库连接对象
String url = "jdbc:mysql://localhost:3306/j_db";
String user = "root";
String password = "123456";
conn = (Connection) DriverManager.getConnection(url, user, password);
// 获取数据库连接对象
stat = (Statement) conn.createStatement();
// 执行sql语句
String sql = "select username from t_user where username='" + username + "' and password='"+ passwd +"'";
System.out.println(sql);
rs = stat.executeQuery(sql);
if (rs.next()) {
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (flag) {
System.out.println("--------------恭喜登录成功-----------------------");
}else {
System.out.println("对不起,你的账户名或者密码错误....");
}
}
}
}
/*
运行结果:
第一次测试:
********************欢迎来到登录界面**********************
输入账号:admin
输入密码:123123
select username from t_user where username='admin' and password='123123'
--------------恭喜登录成功-----------------------
第二次测试:
********************欢迎来到登录界面**********************
输入账号:admin
输入密码:ads 'or' xx '=' xx
select username from t_user where username='amdin' and password='ads 'or' xx '=' xx'
--------------恭喜登录成功-----------------------
*/
1.3 SQL注入分析
从上边的两次运行结果,我们可以看到第二次测试运行的时候,我们输入的密码是错误的但是,却登录成功了,通过打印出来的sql语句可以看到sql语句时一直成立的,这就是所谓的sql注入攻击
2、如何处理SQL注入问题
2.1 PreparedStatement预编译的机制
PreparedStatement先将sql语句的主干传给数据库服务器进行预编译,其中需要用到的参数可以先试用"?"进行占位,之后在使用预编译对象的方法将参数传给服务器,这个时候无论参数中是否有sql语句的关键字,数据库服务器都只是把他当作字符串参数使用,关键字不会起作用。从而达到防注入问题。
2.2 PreparedStatement的优点
1.可以防止sql注入
2.由于使用了预编译机制,执行的效率要高于Statement;PreparedStatement是一次编译多次执行的,传入的参数不同从而执行不同的语句
3.PreparedStatement是类型安全的,编译期检查传入的参数类型
2.3 PerparedStatement的使用
// 编写sql语句,其中参数的位置使用?进行占位
String sql = "select username from t_user where username = ? and password = ?";
// 进行sql语句的预编译;返回一个预编译对象
ps = conn.prepareStatement(sql);
// 对sql语句进行赋值,其中第一个参数是参数的位置,即第几个参数,第二个参数是传给该位置的值
ps.setString(1, username);
ps.setString(2, passwd);
// 执行sql语句
rs = ps.executeQuery();
3、SQL防注入案例
主要是对上述的登录案例解决了SQL防注入问题
package com.company.jdbc;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;
// 实现从数据库中拿取数据,并模拟实现登录
// 解决sql注入问题
public class JDBCLoginTestPlus {
public static void main(String[] args) throws IOException {
// 接收用户登录是输入的账号和密码
System.out.println("********************欢迎来到登录界面**********************");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("输入账号:");
String username = br.readLine();
System.out.print("输入密码:");
String passwd = br.readLine();
// ---------------------连接数据库-------------------------
Connection conn = null;
PreparedStatement ps = null;
Statement stat = null;
ResultSet rs = null;
boolean flag = false;
try {
// 1、注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、 获取数据库连接对象
String url = "jdbc:mysql://localhost:3306/j_db";
String user = "root";
String password = "123456";
conn = (Connection) DriverManager.getConnection(url, user, password);
// 3、定义sql语句框架
String sql = "select username from t_user where username = ? and password = ?";
// 4、进行sql语句的预编译;返回一个预编译对象
ps = conn.prepareStatement(sql);
// 5、对sql语句进行赋值
ps.setString(1, username);
ps.setString(2, passwd);
// 6、执行sql语句
rs = ps.executeQuery();
if (rs.next()) {
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 关闭资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (flag) {
System.out.println("--------------恭喜登录成功-----------------------");
}else {
System.out.println("对不起,你的账户名或者密码错误....");
}
}
}
}
/*
第一次测试:
********************欢迎来到登录界面**********************
输入账号:admin
输入密码:123123
--------------恭喜登录成功-----------------------
第二次测试:
********************欢迎来到登录界面**********************
输入账号:admin
输入密码:abc 'or xx '=' xx
对不起,你的账户名或者密码错误....
*/
转载:https://blog.csdn.net/qq_45061361/article/details/106569727
查看评论