Mysql数据库的安全性问题——注入攻击以及解决办法

在日常生活中我们在网上购物、出行买票等一系列的操作时都需要很重要的一步,那就是先进入自己的个人账户,里面有更详尽的且别人看不到的私有个人信息,如果别人能够进入我们的个人账户,那就杯具了,本文将介绍一种安全性的隐患——仅通过获取正确的用户名就可以进入他人账户,它就是注入攻击,是Mysql数据库中内部机制产生的一种攻击方式。
注入攻击产生的原因是这样的:由于sql语句是由前台参数与后台语句拼接而来,在用户传入的参数位置可能会输入数据库的关键字。这些关键字可能是sql语句的语义发生改变,从而达到一些特殊的效果,这种操作方式称之为sql注入攻击。
由于大量用户的账号登录信息是存储在数据库中的,在输入用户信息时是一个动态的过程,后台设计登录与验证的程序,会根据用户输入的信息去查询数据库中的字段是否匹配,验证通过即可进入个人账户,在用后台程序执行sql语句更新或者查询数据库时,这些sql语句是由前台参数和后台语句拼接的,若用户名中的一些字符恰好是数据库的关键字时,会改变sql语义导致执行sql语句时发生特殊的效果。本文以数据库中关键字#为例,设计一个demo来表述sql语义改变而产生的特殊效果,以及会给出对应的解决方案。
A:注入攻击示例
1.在Mysql中创建database mydb4,再创建table users
在这里插入图片描述
2.用java执行sql语句,执行时是把前台参数(用户信息)和后台语句拼接

 import java.sql.Connection; 
 import java.sql.ResultSet; 
 import java.sql.Statement;
 import java.util.Scanner; 
 import com.mysql.jdbc.PreparedStatement; 
 public class Login { 
 public static void main(String[] args) {
	  Scanner sc=new Scanner(System.in); 
	 System.out.println("请输入用户名:"); 
	 String username=sc.nextLine(); 
	 System.out.println("请输入密码:"); 
	 String password=sc.nextLine(); 
	 testLogin(username,password); 
	 } 
 private static void testLogin(String username, String password) { 
	 Connection conn=null; 
	 Statement stat=null;
	  ResultSet rs=null;
	   try { 
	   conn=JDBCUntils.getConnection();// ② 数据库驱动jdbc,抽取成一个方法来使用 
	   stat=conn.createStatement(); //①npk 做下标记
	    rs=stat.executeQuery("select * from users where username='"+username+"'and password='"+password+"'"); 
	    if(rs.next()){//如果为true,则证明能查询到用户名,登录进去 
	    System.out.println("登陆成功");
	     }else{ 
   	  System.out.println("登陆失败,请重新登陆"); 
     }
     	 } catch (Exception e) {
      	 e.printStackTrace(); 
       } finally{ 
       JDBCUntils.close(conn, stat, rs); } 
       }
        } 

3.接着我们看下运行结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述都是和数据库匹配后得到的效果,是没问题的。
4.接着我们在以下这个结果
在这里插入图片描述
怎么会这个样子呢。在输入密码那步我是按enter键跳过执行的,在得知一个真实的用户名后,在用户名后加了‘#,未输密码依旧“成功”进入一个账户中,如果这是一个储蓄账户,想想就后背发凉吧。
那我们分析传入参数(npk’#)依旧登录成功的原因:
由于前台传入的参数 与后台的sql语句动态拼接,在①npk行的代码中是查询sql语句,sql语句本义是 select * from user where username=‘username’ and password=‘password’;
可是为了实现动态拼接"select * from users where username=’"+username+"‘and password=’"+password+"’";当输入(npk’#)我们可以把整个拼接语句进行拆解,
拆解"select * from users where username=’",中是"" 引的是字符串,
//“select * from users where username=’”+npk‘#是读到了,但是(npk’#)中的’直接和第一个’拼接,组成username=‘npk’,而"‘and password=’"+password+"’“拆解为”‘and password=’" 与 password 和 “’” (空串),由于#时数据库中的关键字,注释了参数password
而最终的sql语义最终变成了select * from user where username=‘npk’# ‘and password=’
password '; 参数password没有读到,因此就会出现这种情况: 一旦正确输入用户名,加上 '# ,关键字#把要传入的参数注释了,uername正确读到,password为空串,“登陆成功”。
之前某路出行买票软件也因此出现个例,后来就改好了。
B:解决方案–利用Statement子接口PreparedStatement防止sql注入攻击
PreparedStatement由于具有预编译的功能,先将sql语句的主干部分发送到数据库服务器中,参数位置使用"?"预留,sql主干语句到达服务器中时,会立刻变成一段机器码(二进制数据),这个机器码不能被操作。
PreparedStatement发送sql 的步骤:先传sql语句主干,在把参数发送到数据库服务器,这些参数到达数据库服务器后时只会作为纯文本内容使用(比如关键字**#**只是text文本“#”),这样一来就不会使sql语义变化而注入攻击了。
以下是利用PreparedStatement来预编译防止注入攻击的代码:

//导包没变
 public class Login {
  public static void main(String[] args) { 
	  Scanner sc=new Scanner(System.in); 
	  System.out.println("请输入用户名:"); 
	  String username=sc.nextLine(); 
	  System.out.println("请输入密码:"); 
	  String password=sc.nextLine(); 
	  PreparedtestLogin(username,password); 
	  } 
  private static void PreparedtestLogin(String username, String password) { 
	  Connection conn=null; 
	  PreparedStatement ps=null; 
	  ResultSet rs=null; 
	  try{ 
 	 conn=JDBCUntils.getConnection(); 
  	ps= (PreparedStatement) conn.prepareStatement("select * from users where username=? and password=?"); 
  	ps.setString(1, username);
  	 ps.setString(2, password); 
  	 rs=ps.executeQuery(); 
	   if(rs.next()){
	   System.out.println("登陆成功"); 
	   }else{ 
	   System.out.println("登陆失败,请重新登陆");
	    } 
	   } catch (Exception e) { 
	e.printStackTrace();
	 }finally{
 	 JDBCUntils.close(conn, ps, rs); 
  } 
  }

运行结果如下:
在这里插入图片描述
以上就是出现问题和解决的过程。 ps:其中第一个代码片中②行是数据库驱动jdbc,驱动的6部曲,我利用工厂模式建了一个JDBCuntils类,抽取了获取连接和关闭资源方法来简单配合着使用。 有空再把数据库驱动jdbc和工厂模式在整理下。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值