sql的注入原因和解决办法

achong
2022-04-30 / 1 评论 / 134 阅读 / 正在检测是否收录...

sql注入

原因:sql注入的根本原因是用户输入的信息中含有sql语句的关键字,并且这些关键字参与了sql语句的编译过程,导致sql的原意被扭曲,进而达到sql注入。

例如以下查询方法。

 //该方法查询数据库是否有此账号密码,返回一个布尔值
    private static boolean userQuery(String userName, String userPassword) {
        boolean flag = false;
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = DBUtils.getConnection();
            stmt = conn.createStatement();
            //执行sql语句
            String sql = "select * from userinfo where userName='"+userName+"' and userPassword='"+userPassword+"'";
            rs = stmt.executeQuery(sql);
            
            if (rs.next()) flag = true;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DBUtils.close(conn,stmt,rs);
        }
        return flag;
    }

用一个变量接收用户输入的密码,如果接收到的是一个正常的密码:123456,那么拼接后的sql语句就是正常的。

select * from userinfo where userName='王五' and userPassword='123456'

但是

如果接收到的密码不是一个正常的密码,而是 abc' or '1'='1。这样的不正常密码带有sql的关键字or,再加上巧妙的组合,最终会使拼接后的sql语句就是这样的。

分析以下sql的条件语句:因为or后面的条件 '1'='1' 是永远是正确的,所以前面的条件userName='123' and userPassword='abc' 可以无视。即使用户输入的账号密码不正确或者不存在,这条sql语句也会正常成功执行。且有返回值。

select * from userinfo where userName='123' and userPassword='abc' or '1'='1'

解决sql注入

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

  1. 首先,将Statement接口改成子接口PreparedStatement。
  2. 在编写sql语句的时候,在关键位置用 "?" 占位,这里吧?叫做占位符
  3. 在JDBC第三步,获取数据库操作对象的时候,传入sql语句。这步是把sql预处理,也就是搭起sql语句的框架。
  4. 下一步使用setString方法设置占位符对应的关键字。索引从1开始。

    1. 注意:字符串用setString() , int类型数据用setInt()
  5. 最后,执行sql就ok。不过还要注意,此时已经不需要传入sql语句,因为前面的预编译和设置占位就已经完成了。

修改后的jdbc过程。

//该方法查询数据库是否有此账号密码
    private static boolean userQuery(String userName, String userPassword) {
        Connection conn = null;
        //将Statement接口改成PreparedStatement接口,这俩是继承关系。
        PreparedStatement stmt = null;
        ResultSet rs = null;
        boolean flag = false;

        try {
            conn = DBUtils.getConnection();
            //1.在sql语句的关键位置用?占位。
            String sql = "select * from userinfo where userName=? and userPassword=?";
            //2.预编译sql,也就是按处理好sql的框架
            stmt = conn.prepareStatement(sql);
            //3.setString方法设置占位符对应的关键字,索引从1开始
            stmt.setString(1,userName);
            stmt.setString(2,userPassword);
            //4.执行查询,但是这里不需要传sql
            rs = stmt.executeQuery();
            
            if (rs.next()) flag = true;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }  finally {
            DBUtils.close(conn,stmt,rs);
        }
        return flag;
    }

虽然Preparedment的操作多一点,但是安全。

因为它是先把带有 "?" 占位符的sql语句先编译了,然后通过setString方法在传入占位符对应的值,这样做即使传入的值带有mysql的关键字也没事,因为此时的sql语句已经预编译了,后面传入的值没有参与编译过程。

如果用户还是传入不规范的数据,则sql执行会报错。

1

评论 (1)

取消
  1. 头像
    achong 作者
    Windows 10 · Google Chrome

    画图

    回复