JDBC的SQL注入攻击

SQL注入问题

        • 1、SQL注入原理
            • 1.1 SQL注入攻击:
            • 1.2 SQL注入案例
            • 1.3 SQL注入分析
        • 2、如何处理SQL注入问题
            • 2.1 PreparedStatement预编译的机制
            • 2.2 PreparedStatement的优点
            • 2.3 PerparedStatement的使用
        • 3、SQL防注入案例

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 
对不起,你的账户名或者密码错误....
*/

你可能感兴趣的:(JDBC)