SQL注入问题的解决(PreparedStatement)

sql注入的问题

上篇博客用户登录业务简易实现结尾提到了代码的不足:存在SQL注入问题。

下面演示一下什么是SQL,注入问题。(就拿上篇的代码举个例子)

在这里插入图片描述
在这里插入图片描述

以上为正常现象,但有些“不法分子”抓住了代码的漏洞,干了一些不为人知的事情。

2020倒计时震惊趣味动态表情包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJYoyxHC-1637297168676)(C:/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20211119120823592.png)]

What!竟然登录成功了!发生了甚么?让我们Debug一下,一探究竟。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLFMdPFu-1637297168677)(C:/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20211119121211414.png)]

select * from t_user where username = ‘jiuzhe’ and userpwd = ‘jiuzhe’ or ‘1’=‘1’

‘1’='1’为true,从而导致了整个柿子返回结果为true。

在这里插入图片描述

如何解决sql注入的问题(PreparedStatement)

导致SQL注入的根本原因是什么?

​ 用户输入的信息中含有SQL语句的关键字,最为致命的是这些关键字参与了SQL语句的编译过程

​ 导致SQL语句的原意被扭曲,进而达到SQL注入。

如何解决SQL注入的问题?

​ 只要用户输入的信息不参与SQL语句的编译过程,问题就解决了。

​ 即使用户输入的信息含有SQL语句的关键字,但由于不参与SQL语句的编译过程,那么也起不到作用。

​ 为此,我们要使用Java.sql.PreparedStatement。

​ PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。

修改后的代码:

public class userLogin2 {
    
    
    public static void main(String[] args) {
    
    
        // 初始化界面,返回用户的信息。
        Map<String, String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "Success" : "False");
    }

    private static boolean login(Map<String, String> userLoginInfo) {
    
    
        // 打一个标记
        boolean loginSuccess = false;

        String loginName = userLoginInfo.get("userLoginName");
        String loginPwd = userLoginInfo.get("userLoginPwd");

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
    
    
            // 1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/userdata",
                    "root", "root");
            // 3.获取预编译的数据库操作对象
            // SQL语句的框架,其中一个?,表示一个占位符,一个?将来接收一个“值”,注意占位符不能用单引号括起来。
            String sql = "select * from t_user where username = ? and userpwd = ?";// sql语句的框架
            // 程序运行到此处,会发送sql语句框架传给DBMS,然后DBMS对sql语句进行预编译。
            ps = conn.prepareStatement(sql);
            // 给占位符?传值(第一个?下标为1,第二个?下表为2。。。。。。)
            ps.setString(1, loginName);
            ps.setString(2, loginPwd);
            // 4.执行sql语句
            rs = ps.executeQuery();
            // 5.处理查询结果集
            if (rs.next()) {
    
    
                loginSuccess = true;
            }
        } catch (ClassNotFoundException | SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            // 6.关闭资源
            if (rs != null) {
    
    
                try {
    
    
                    rs.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (ps != null) {
    
    
                try {
    
    
                    ps.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (conn != null) {
    
    
                try {
    
    
                    conn.close();
                } catch (SQLException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }

    private static Map<String, String> initUI() {
    
    
        // 获取用户信息
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入账号:");
        String userLoginName = sc.nextLine();
        System.out.println("请输入密码:");
        String userLoginPwd = sc.nextLine();
        Map<String, String> userLoginInfo = new HashMap<>();
        // 将用户信息存入Map中
        userLoginInfo.put("userLoginName", userLoginName);
        userLoginInfo.put("userLoginPwd", userLoginPwd);
        return userLoginInfo;
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwJnCbIE-1637297168677)(C:/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20211119124109969.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3saUDWR-1637297168678)(C:/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20211119124203072.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x8u8KE64-1637297168678)(C:/Users/86182/AppData/Roaming/Typora/typora-user-images/image-20211119124436890.png)]

对于不法分子,我想说:就这?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eOxj7N16-1637297168679)(file:///C:\Users\86182\Documents\Tencent Files\2642115441\ExpressionRecommend\1637296962375_53620D4EC78F\E71B46546794BDD13BA0FD4ADB8C6A17)]

Statement和PreparedStatement的对比

  • Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题。
  • Statement是编译一次执行一次,而PreparedStatement是编译一次,可执行N次,效率较高一些。
  • PreparedStatement在编译过程中进行类型的安全检查。

我们以后大部分情况下,都会使用PreparedStatement。

只有在业务方面必须支持SQL注入,进行SQL语句拼接的时候,必须使用Statement。

猜你喜欢

转载自blog.csdn.net/Sherlook_Holmes/article/details/121419979