JDBC技术详解(二)--sql注入攻击问题

本文介绍SQL注入攻击原理及如何利用PreparedStatement防止SQL注入攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上篇文章中 《 JDBC技术详解(一)--入门及其API 》,提到的使用statement对象执行字符串拼接的sql会存在注入攻击漏洞。这篇文章,我们来解决这个问题。

首先,什么是sql注入攻击?

所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令,比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

通俗的说:sql注入攻击是指,在使用字符串拼接sql语句时,恶意拼接带sql语言关键词,使得sql语句的原意改变,达到恶意攻击的目的。

下面我们通过案例(查询指定用户)来演示sql注入攻击:

在数据库中有如下一张User表,我要根据name查询用户信息。

代码如下:

			package jdbc.test;			
			import java.sql.Connection;
			import java.sql.DriverManager;
			import java.sql.ResultSet;
			import java.sql.SQLException;
			import java.sql.Statement;
			import com.mysql.jdbc.Driver;			
			public class test
			{
				public static void main(String[] args) throws ClassNotFoundException, SQLException
				{
					// 1、正常赋值情况下
					// String userName = "admin";
					// 2、非法赋值情况下
					String userName = "admin' OR '1'='1 ";
			
					Class.forName("com.mysql.jdbc.Driver");
					String url = "jdbc:mysql://localhost:3306/mydb";
					String username = "root";
					String pwd = "000000";
					Connection con = DriverManager.getConnection(url, username, pwd);
					Statement st = con.createStatement();
					String sql = "select * from user where name='" + userName + "'";
					ResultSet rs = st.executeQuery(sql);
					// 5.对ResultSet进行遍历,得到信息.
					while (rs.next())
					{
						int id = rs.getInt("id");
						String name = rs.getString("name");
						String password = rs.getString("password");
						System.out.println("ID:" + id + "  NAME:" + name + "  password:" + password);
					}
					// 6.关闭资源
					rs.close();
					st.close();
					con.close();
				}
			}


1、正常情况下,我们对userName字段赋值为admin时,拼接后的sql查询语句是:

select * from user where name='admin'

查询结果是我们期望的,只查询出了admin的信息。

2、但是要是给userName字段非法赋值为    admin' OR '1'='1   呢,拼接后的sql查询语句是:

select * from user where name='admin' OR '1'='1' 

这个查询语句的条件就变成了永真,会将所有用户的信息查询出来,如下:

如何解决sql注入攻击漏洞问题?

其实很简单,不再使用字符串去拼接sql语句,而是通过将jdbc中的提供的preparedStatement对象中的参数化查询。下面我们来看下如何使用吧。

statement对象不提供这一功能。但是preparedStatement对象提供了这一功能。步骤如下:

1、先定义好sql语句,参数的地方用“?”作为暂占位符。

2、设置一系列参数,获取到connection对象。

3、通过connection对象获得preparedStatement对象,需要将sql语句作为参数传进去,其内部进行sql预格式化。

4、给占位符赋值。即使用setXxxx( int index, Xxx   param)方法将占位符替换成我们想要的值。

5、执行sql查询。注意,要使用空参的executeQuery() 方法。

不废话了,上例子:

			package jdbc.test;
			
			import java.sql.Connection;
			import java.sql.DriverManager;
			import java.sql.PreparedStatement;
			import java.sql.ResultSet;
			import java.sql.SQLException;
			import java.sql.Statement;
			import com.mysql.jdbc.Driver;
			
			public class test2
			{
				public static void main(String[] args) throws ClassNotFoundException, SQLException
				{
					// 正常赋值情况下
					String userName = "admin";
					// 非法赋值情况下
					// String userName = "admin' OR '1'='1 ";
			
					// 1、先定义好sql语句,其中需要使用参数的地方使用 "?" 来占位。
					String sql = "select * from user where name=?";
			
					Class.forName("com.mysql.jdbc.Driver");
					String url = "jdbc:mysql://localhost:3306/mydb";
					String username = "root";
					String pwd = "000000";
					Connection con = DriverManager.getConnection(url, username, pwd);
					
					// 2、获取能执行sql语句的preparedStatement对象。在获取对象时,需要将sql语句作为参数传进去,其内部会对sql语句进行预格式化处理。
					PreparedStatement pst = con.prepareStatement(sql);
					// 3、使用参数来替换掉sql语句中的问号占位符。
					// 参数1:表示要替换的是第几个问号,从1开始。这里表示第一个问号。
					// 参数2:表示要替换的内容是什么。这里的内容是userName的值,即“admin”
					pst.setString(1, userName);
					// 4、执行查询,注意:这个查询方法是没有参数的。
					ResultSet rs = pst.executeQuery();
			
					// 5.对ResultSet进行遍历,得到信息.
					while (rs.next())
					{
						int id = rs.getInt("id");
						String name = rs.getString("name");
						String password = rs.getString("password");
						System.out.println("ID:" + id + "  NAME:" + name + "  password:" + password);
					}
					// 6.关闭资源
					rs.close();
					pst.close();
					con.close();
				}
			}

你会发现,不管你怎么输入参数,结果都是我们期望的。因为preparedStatement对象中,即使我们传进去的参数有关键的字符,但是它也会将我们传进去的整个字符串作为正真的一个参数,而不是简单的拼接字符串,所以,这样的话,sql语句就不会出现第二种意义了。这也就防止了sql注入攻击。

Statement和PreparedStatement对象的比对:

 

Statement

PreparedStatement

sql语句

sql语句使用字符串拼接

sql语句使用“?”占位符占位

获取对象

不需要参数  con.createStatement();

需要参数  con.prepareStatement(sql);

给参sql中参数赋值

不需要

setXxxx(索引,值)。

执行查询

 需要sql作为参数  st.executeQuery(sql);

不需要参数   pst.executeQuery();

注意:关于给参数赋值,你得参数是什么类型的,就用对应类型的set方法,例如:

Int型:  setInt(int index,    int value)

String型:setStringt(int index,    String value)


总结:

1、在企业开发中,这两种执行sql的方式都会使用到,但是其侧重点不一样。

2、非参数化查询,适合执行不带条件的sql语句,执行批处理性能相对高。

2、参数化查询,适合执行带条件的sql语句,执行批处理性能相对低。


本文为原创,如需转载,请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值