IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 解决JDBC实现用户登录时出现SQL注入问题详解 -> 正文阅读

[大数据]解决JDBC实现用户登录时出现SQL注入问题详解

JDBC实现用户登录时出现SQL注入问题

JDBC实现用户登录:

public class jdbcTest06{
    public satic void main(String[] args){
		Map<String,String> userLoginInfo initUI(); 
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }
    private static boolean login(Map<String,String> userLoginInfo){
        boolean loginSuccessornot = false;
        
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            // 1. 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2. 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
            // 3. 获取数据库操作对象
            stmt = conn.creatStatement();
            // 4. 执行SQL语句
            String sql = "select * from t_user where loginName '"+loginName+"' and '"+loginPwd+"'";
            rs = stmt.executeQuery(sql);
            // 5. 处理结果集
            if(rs.next()){
                loginSuccessornot = true;
            }else{
                loginSuccessornot = false;
            }
        }catch(SQLException e){
            e.printStackTrace();
        }finally{
            //释放资源
            if(rs != null){try{rs.close();}catch(SQLException e){e.printStackTrace();}}
            if(stmt != null){try{stmt.close();}catch(SQLException e){e.printStackTrace();}}
            if(conn != null){try{conn.close();}catch(SQLException e){e.printStackTrace();}}}
        return loginSuccessornot;
    }
    private static Map<String,String> iniUT(){
        Scanner s = new Scanner(System.in);
        System.out.println("用户名:");
        String loginName = s.nextLine();
        System.out.println("密码:");
        String loginPwd = s.nextLine();
        
        Map<String,String> userLoginInfo = new HaspMap<>();
        userLoginInfo.put("loginName","loginName");
        userLoginInfo.put("loginPwd","loginPwd");
        return userLoginInfo;
    }
}

??


SQL注入

当前程序存在问题:

??输入:
????用户名:fdsa
????密码:fdsa’ or ‘1’ = ‘1
??输入:
????登录成功 ( 数据库中不存在这条数据 )

这种现象被成为SQL注入

问题代码块:

jade,mysql

        Statement stmt = null;

		// 3. 获取数据库操作对象
        stmt = conn.creatStatement();
        // 4. 执行SQL语句
        String sql = "select * from t_user where 
        	'"+loginName+"' and '"+loginPwd+"'";
        rs = stmt.executeQuery(sql);

以上代码块解析:

??获取数据库操作对象 stmt 后执行SQL语句将传了loginName 和 loginPwd 两个参数的SQL语句作为参数传给了 executeQuery() 方法
??然后 executeQuery() 方法进行编译,就会把 fdsa’ or ‘1’ = ‘1 作为sql语句一起编译了,

看看编译后的sql语句:

 sql: "select * from t_user where 
 	loginName = 'fdsa' and loginPwd = 'fdsa' or '1' = '1'"

??可以看到查询语句的where条件变成了
???? loginName = 'fdsa' and loginPwd = 'fdsa' or '1' = '1'"

??意思就是 where(loginName = 'fdsa' and loginPwd = 'fdsa') 或者 ('1' = '1')

??而'1' = '1'恒成立,所以无论怎么查询都能查到结果,甚至是将所有的用户账号密码都查了出来,最后完成登录

所以出现SQL注入的关键是:
??用户输入的非法信息被数据库操作系统编译了,例如用户输入了含有SQL语句的关键字,并且这些关键字参与了SQL语句的编译过程
??


解决SQL注入

??只要用户提供的信息不参与SQL编译过程,即使提供的信息含有关键字等非法信息,但是只要不参加数据库操作系统的编译九不会形成注入

??要想用户信息不参与SQL语句的编译,那必须适合用java.sql.PreparedStatement

??PreparedStatement 接口集成了java.sql.Statement,PreparedStatement 是属于预编译的数据库操作对象

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

解决SQL注入的关键是:
??用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用

解决注入代码:

package com.hyqwsq.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
实现功能:
    1. 需求:模拟用户登录功能的实现
    2. 业务描述:
        程序运行的时候提供一个数据的入口,可以让用户输入用户名和密码,
        用户数据用户名和米啊后,提交信息,Java程序收集到用户信息
        Java程序连接数据库验证用户名是否合法
        合法:显示登录成功
        不合法:显示登录失败
    3. 数据准备
        在实际开发中,表的设计会使用专业的建模工具,我用的使PoerDesigner,
        使用PD工具进行数据库表的设计,(附件user-login.sql脚本);
    4. 当前程序存在问题:
        用户名:fdsa
        密码:fdsa' or '1' = ‘1
        登录成功
        这种现象被成为SQL注入
    5. 导致SQL注入的根本原因:
        将密码:fdsa' or '1' = ‘1传进去后的SQL语句:
            sql: "select * from t_user where loginName = 'fdsa' and loginPwd = 'fdsa' or '1' = '1'"
            查询条件变成了:where(loginName = 'fdsa' and loginPwd = 'fdsa')  or  ('1' = '1')
            前面的用户名和用户密码不用对,后面的’1‘ = ’1‘恒成立,导致这条SQL语句一定能查到数据,所以一定可以登录成功
        原因:用户输入的非法信息被数据库操作系统编译了,例如用户输入了含有SQL语句的关键字,并且这些关键字参与了SQL语句的编译过程
        导致SQL语句的原意被扭曲,进而形成SQL注入
    6. 解决SQL注入:
        只要用户提供的信息不参与SQL编译过程
        即使提供的信息含有关键字等非法信息,但是只要不参加数据库操作系统的编译九不会形成注入
        要想用户信息不参与SQL语句的编译,那必须适合用java.sql.PreparedStatement
        PreparedStatement 接口集成了java.sql.Statement
        PreparedStatement 是属于预编译的数据库操作对象
        PreparedStatement 的原理是预先对SQL语句的框架进行编译,然后再给SQL语句传值
    7. 解决SQL注入的关键是:
        用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用
 */
public class jdbcTest06 {
    public static void main(String[] args) {
        // 初始化一个界面,创建一个可以初始化界面的方法,
        // 接收的返回值是用户名和密码,放到一个MAP容器中
        Map<String,String> userLoginInfo = initUI();

        // 验证用户和密码并登录,创建登录方法,传进用户信息,userLoginInfo为参数
        // 登录失败或者成功只有两种结果,所以方法返回一个布尔类型就好
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false 表示失败,true 表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        // 打标记
        boolean loginSuccessornot = false;

        // 单独定义变量
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");

        //JDBC代码
        Connection conn = null;
        // Statement stmt = null;
        // 换成预编译的数据库操作对象 PrepareSratement
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            // 1.注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hyqwsq","root","13903014064");
            // 3.获取编译的数据库操作对象
            // 将原来的值换成占位符 ?
            // 这样sql语句变成了框架,? 表示占位符,一个占位符将接收一个值,占位符不能使用单引号括起来
            String sql = "select * from t_user where loginName = ? and ?";
            // 程序执行到这里,会发送SQL语句框架给DBMS,然后DBMS进行SQL语句的预先编译
            ps = conn.prepareStatement(sql);
            // 给占位符 ? 传值(第一个占位符的下标是1, 第二个占位符的下标是2,JDBC中所有下标从1开始)
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            // getString()方法传进去字符串后会自动加上单引号,如果是其getInt()就不会

            // 4.执行SQL
            //把用户数据的用户名的用户密码的字符串拼到字符串中:双引号中间加两个加号
            //String sql = "select * from t_user where loginName = '"+userLoginInfo.get("loginName")+"' and loginPwd = '"+userLoginInfo.get("loginPwd")+"'";

            //单独定义变量后
            // String sql = "select * from t_user where loginName = '"+loginName+"' and '"+loginPwd+"'";
            // 以上正好完成了SQL语句的拼接,以下代码的含义是:发送SQL语句给DBMS,DBMS进行编译
            // 正好将用户提供的非法信息编译减去,导致了原来的SQL语句含义被扭曲了
            rs = ps.executeQuery(sql);

            // 5.处理结果集
//            while(rs.next()){}
            //不需要循环查看结果集,因为返回值为布尔类型,只需要看有没有结果集就好
            if(rs.next()){
                //登录成功
                loginSuccessornot = true;
            }


        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }finally {
            // 6.关闭通道,释放资源
            if(rs != null){
                try {rs.close();} catch (SQLException throwables) {throwables.printStackTrace();}}
            if(ps != null){
                try {ps.close();} catch (SQLException throwables) {throwables.printStackTrace();}}
            if(conn != null){
                try {conn.close();} catch (SQLException throwables) {throwables.printStackTrace();}}
        }

        return loginSuccessornot;
    }

    /**
     * 初始化用户界面
     * @return 返回用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);
        //输入用户名
        System.out.println("用户名:");
        String loginName = s.nextLine();
        // 输入密码
        System.out.println("密码:");
        String loginPwd = s.nextLine();

        //将数据组装到嘛Map中
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName","loginName");
        userLoginInfo.put("loginPwd","loginPwd");
        return userLoginInfo;
    }


}

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 08:55:06  更:2021-11-26 08:56:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:49:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码