java学习笔记38(sql注入攻击及解决方法)

时间:2023-03-08 23:14:35
java学习笔记38(sql注入攻击及解决方法)

上一篇我们写了jdbc工具类:JDBCUtils ,在这里我们使用该工具类来连接数据库,

在之前我们使用 Statement接口下的executeQuery(sql)方法来执行搜索语句,但是这个接口并不安全,容易被注入攻击,注入攻击示例:

首先我们需要一个存放登录用户名密码的表:

use qy97;
create table login(
id int primary key auto_increment,
sname varchar(50),
pwd varchar(50)
);
insert into login values
(1,'zhangsan',''),
(2,'lisi','');

然后我们写代码实现登陆:

package com.zs.Demo;

import JDBCUtils.JDBCUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner; public class SQLZhuRu {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
login(name,pwd);
} private static void login(String name, String pwd) {
Connection conn = JDBCUtils.getConnection();
String sql = "select * from login where sname='"+name+"'and pwd='"+pwd+"';";
System.out.println(sql);
Statement stat = null;
ResultSet rs=null;
try {
stat = conn.createStatement();
rs = stat.executeQuery(sql);
if (rs.next()) {
System.out.println("登陆成功,欢迎" + rs.getString("sname"));
} else {
System.out.println("登录失败!!");
}
} catch (SQLException e) {
e.printStackTrace();
} JDBCUtils.close(conn,stat,rs);
}
}

运行结果如下:

java学习笔记38(sql注入攻击及解决方法)

输入账号密码,然后在数据库中搜索,搜索到数据则登录成功,否则登录失败:

java学习笔记38(sql注入攻击及解决方法)

可是,当我们这样输入时,也能登陆成功:

java学习笔记38(sql注入攻击及解决方法)

从上面语句可以看出,执行的sql与语句是:select * from login where sname='lisi'and pwd='789 'or'1=1';

1=1是恒成立的,or 只要两边有一个为true 则为 true  ,所以登录成功,这是一个最简单的注入攻击,

解决方法:使用prepareStatement接口,以上一个例子为基础做修改

package com.zs.Demo;

import JDBCUtils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner; public class PerpareStatementDemo {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
try {
login(name,pwd);
} catch (Exception e) {
e.printStackTrace();
}
}
//传递变量name pwd给方法
private static void login(String name, String pwd) throws Exception {
Connection conn = JDBCUtils.getConnection();
// 设置?占位置,将查询的参数用?来替换
String sql="select * from login where sname=? and pwd=?";
PreparedStatement pre = conn.prepareStatement(sql);
// setObject方法来设置占位的?的值
pre.setObject(1,name);//设置第一个?的值为变量name
pre.setObject(2,pwd);//设置第二个?的值为变量pwd
ResultSet rs = pre.executeQuery();
if (rs.next()) {
System.out.println("登陆成功");
} else {
System.out.println("登录失败");
}
JDBCUtils.close(conn, pre, rs);
}
}

运行结果:

java学习笔记38(sql注入攻击及解决方法)

可以看出,在不改变sql语句的情况下将?替换为变量的值,当注入攻击时:

java学习笔记38(sql注入攻击及解决方法)

可以看出来这个接口更加安全,所以建议使用这个接口来实现增删改查;关于PrepareStatement接口:(官方文档)

  PrepareStatement接口:

    表示预编译的SQL语句的对象。

    SQL语句已预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。

    注意:setter方法( setShortsetString用于设置IN参数值必须指定与所定义的SQL类型的输入参数的兼容的类型,等等)。 例如,如果IN参数具有SQL类型INTEGER ,   则应使用方法setInt

使用PrepareStatemnet接口实现数据库的更新:

package com.zs.Demo;

import JDBCUtils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement; public class PrepareStatementDemo2 {
public static void main(String[] args) {
Connection conn = JDBCUtils.getConnection();
String sql="update login set sname = ? where id=?;";
PreparedStatement pst =null;
try {
pst = conn.prepareStatement(sql);
pst.setObject(1, "dijia");
pst.setObject(2,1);
pst.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.close(conn, pst);
}
}
}