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','111'),
(2,'lisi','123');

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

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);
    }
}

运行结果如下:

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

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

从上面语句可以看出,执行的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);
    }
}

运行结果:

 

可以看出,在不改变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);
        }
    }
}
原文地址:https://www.cnblogs.com/Zs-book1/p/10638578.html