ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

JDBC防止SQL注入攻击:PreparedStatement

2021-12-09 22:01:05  阅读:145  来源: 互联网

标签:username account JDBC String resultSet PreparedStatement SQL null


目录

Sql注入

SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据,致使非法数据侵入系统。

以模拟登录为例:在前台输入用户名和密码,后台判断信息是否正确,并给出前台反馈信息,前台输出反馈信息。

public class TestInjection {
    private static String driver ="com.mysql.cj.jdbc.Driver";
    private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
    private static String user="root";
    private static String password="root";
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
        System.out.println("请输入用户名");
        String username=sc.next();
        System.out.println("请输入密码");
        String pwd =sc.next();
        Account account = getAccount(username, pwd);
        System.out.println(null!= account?"登录成功":"登录失败");
        sc.close();
    }
    public static Account getAccount(String username,String pwd){
        Connection connection = null;
        Statement statement=null;
        ResultSet resultSet=null;
        Account account =null;
        try{
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user,password);
            statement = connection.createStatement();
            String sql="select * from account where username ='"+username+"' and password ='"+pwd+"'";
            System.out.println(sql);
            resultSet = statement.executeQuery(sql);
            while(resultSet.next()){
                int aid = resultSet.getInt("aid");
                String usernamea = resultSet.getString("username");
                String pwda = resultSet.getString("password");
                double money = resultSet.getDouble("money");
                account=new Account(aid,usernamea,pwda,money);
                System.out.println(account);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(null != resultSet){
                try {
                    resultSet.close();
                } catch (SQLException e) {e.printStackTrace();
                }
            }
            if(null != statement){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(null != connection){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return account;
    }
}

当输入下面的值时:

username=aaa,pwd=abc'or'aa'='a

即使是错误的,也能登录成功。让登录功能形同虚设,这就是SQL注入风险。这是为什么呢?原因在于SQL语句是字符串拼接的。SQL语句中拼接的内容破坏了SQL语句原有的判断逻辑。

使用PreparedStatement预编译语句对象解决SQL注入攻击

public class TestInjection2 {
    private static String driver ="com.mysql.cj.jdbc.Driver";
    private static String url="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";
    private static String user="root";
    private static String password="root";
    public static void main(String[] args) {
        Scanner sc =new Scanner(System.in);
        System.out.println("请输入用户名");
        String username=sc.next();
        System.out.println("请输入密码");
        String pwd =sc.next();
        Account account = getAccount(username, pwd);
        System.out.println(null!= account?"登录成功":"登录失败");
        sc.close();
    }
    public static Account getAccount(String username,String pwd){
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet=null;
        Account account =null;
        try{
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user,password);
            /*
            * 1使用PreparedStatement语句对象防止注入攻击
            * 2PreparedStatement 可以使用 ? 作为参数的占位符
            * 3使用?作为占位符,即使是字符串和日期类型,也不使用单独再添加 ''
            * 4connection.createStatement();获得的是普通语句对象 Statement
            * 5connection.prepareStatement(sql);可以获得一个预编译语句对象PreparedStatement
            * 6如果SQL语句中有?作为参数占位符号,那么要在执行CURD之前先设置参数
            * 7通过set***(问号的编号,数据) 方法设置参数
            * */
            String sql="select * from account where username = ? and password = ?";
            preparedStatement = connection.prepareStatement(sql);//这里已经传入SQL语句
            //设置参数
            preparedStatement.setString(1,username );
            preparedStatement.setString(2,pwd );
            //执行CURD
            resultSet = preparedStatement.executeQuery();// 这里不需要再传入SQL语句
            while(resultSet.next()){
                int aid = resultSet.getInt("aid");
                String usernamea = resultSet.getString("username");
                String pwda = resultSet.getString("password");
                double money = resultSet.getDouble("money");
                account=new Account(aid,usernamea,pwda,money);
                System.out.println(account);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(null != resultSet){
                try {
                    resultSet.close();
                } catch (SQLException e) {e.printStackTrace();
                }
            }
            if(null != preparedStatement){
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(null != connection){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return account;
    }
}

prepareStatment对象在set***方法上,会对单引号进行转译处理,也就是说,?中的数据的单引号 ‘ 会被转义成 \’,这样就单引号就不会破坏sql语句的结构。

SELECT * FROM users WHERE userName = ? AND password = ?
preparedStatement.setString(1,"xiaoming");
preparedStatement.setString(2,'anything'   OR 'x'='x');
会被转义为
SELECT * FROM users WHERE userName = 'xiaoming' AND password = 'anything\' OR\'x\'=\'x\''
而不是
SELECT * FROM users WHERE userName = 'xiaoming' AND password = 'anything' OR 'x'='x'

PreparedStatement把值当中的所有单引号给转义了,这就达到了防止sql注入的目的,mysql驱动的PreparedStatement实现类的setString();方法内部做了单引号的转义。
Statement不能防止sql注入,因为它没有把单引号做转义,而是直接拼接字符串,所以达不到防止sql注入的目的。

标签:username,account,JDBC,String,resultSet,PreparedStatement,SQL,null
来源: https://blog.csdn.net/qq_41587516/article/details/121844790

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有