数据库开发 - SQL注入与防范

#数据库泄露的风险 由于数据库通常存储的是业务的价值数据,例如用户信息、交易信息,如果这些内容泄露后果不堪设想。

携程SQL注入漏洞
输入图片说明

新浪网SQL漏洞
输入图片说明

#数据库注入 在Web应用架构下,终端用户无法直接访问数据库。他们必须通过发送HTTP请求,到Java应用服务器,然后由Java应用服务器来访问后端数据库。所以,恶意用户想要获取数据库中的核心价值数据,就绕不开Java Web应用程序。他们唯一的途径,就是利用程序漏洞,伪装自己的请求,欺骗业务程序,达到最终获取数据库数据的目的。

##被注入代码示例 这段程序中,根据用户名密码去后端数据库查询user表,查看是否用根据用户名密码匹配的用户。如果数据库记录返回的不为空,这个应用程序也会返回一个不为空的User对象。将信息返回调用者。 输入图片说明

##注入过程 ###程序正常运行 输入图片说明

###简单注入,跳过用户密码 用户添加了;导致确认前半段为SQL语句,后半段的--表示被注释掉了,则最终的SQL执行,并没有经过密码的验证功能。 , 利用Java程序动态拼接SQL的漏洞,篡改了SQL语义,欺骗了服务器,恶意的获取了数据库中的数据。 输入图片说明

##SQL注入 用户在输入URL或者表单中,输入SQL命令。达到欺骗服务器的目的,篡改原有的SQL语义,发送恶意的SQL到后端,导致后端数据库信息泄露的漏洞。我们称之为SQL注入。

##问题根源 SQL注入的问题根源,在于SQL语句是动态拼接而成。在用户输入参数前,我们SQL语义本身是不确定的。如果用户输入的参数,夹带着SQL命令的特殊字符,会导致原有的SQL语义发生改变。举例
输入图片说明
原始SQL语义,是通过两个过滤条件,确认用户信息。但篡改后,只执行了一个SQL过滤条件。

##解决方案

###PrepatedStatement对象 由于是动态拼接SQL造成的漏洞。首先我们应当确定SQL的语义,然后要保证能够传入的SQL参数,不改变SQL语义。我们可以通过connection.preparedStatement(sql)来创建preparedStatement对象,preparedStatement对象实现了Statement接口的所有方法,但是相对于Statement最大的优势提供了参数化的SQL的方式。我们调用preparedStatement方法,传入格式化的SQL语句。格式化SQL是指所有外部需要出入的参数都使用?代替。这样我们就生成了preparedStatement对象。也就是说SQL语义伴随着获取对象,确定了语义。?代替了参数,实现了占位符的功能。connection.preparedStatement(sql)主要确定SQL的语义。

输入图片说明

###PrepatedStatement传入参数 我们按照格式化参数,从左到右的出现顺序。根据参数类型,如果是Int我们就使用setInt(),如果是String我们就使用setString(),传入参数的函数有两个参数,一个是序号,参数从左到右的出现顺序。在下图例子中userName是在password前面,userName参数是1,password参数是2。第二个就是我们所要输入具体的值。经过上面函数所需要的两个参数,我们就把参数的值传入到了SQL里面。并且这个参数可以保证不能变更SQL的语义,完成了参数的注入。PrepatedStatement是最基本也是最常用的预防SQL注入的方法。

输入图片说明

##实例测试 ###初始化数据库

CREATE TABLE `user_login` (
`Id`  int NOT NULL AUTO_INCREMENT ,
`userName`  varchar(100) NULL ,
`sex`  int NULL ,
`password`  varchar(100) NULL ,
PRIMARY KEY (`Id`)
)
;


INSERT INTO `user_login` (`Id`, `userName`, `sex`, `password`) VALUES ('1', 'Zhangsi', '0','123456');
INSERT INTO `user_login` (`Id`, `userName`, `sex`, `password`) VALUES ('2', 'LiSan', '0','123456');
INSERT INTO `user_login` (`Id`, `userName`, `sex`, `password`) VALUES ('3', 'GuoYi', '0','123456');

###被注入的SQL语句

Class.forName(JDBC_DRIVER);

        try {
            connection = DriverManager.getConnection(DB_URL,USER,PASSWORD);
            statement = connection.createStatement();
            String sql = "SELECT * FROM user_login WHERE userName = '"+ username + "' AND password = " +password;
            resultSet = statement.executeQuery(sql);

            while(resultSet.next())
            {
                user = new User();
                user.setId(resultSet.getInt("id"));
                user.setUserName(resultSet.getString("userName"));
                user.setSex(resultSet.getInt("sex"));
                user.setPassword(resultSet.getString("password"));
            }
        } catch (SQLException e)
        {
            System.out.println(e.toString());
        }
        finally {
            try{
                if(connection != null) connection.close();
                if(statement != null)statement.close();
                if(resultSet != null)resultSet.close();

            }catch (SQLException e)
            {

            }
        }

###注入测试

    public void testLoginError() throws Exception {
        User user = Login.login("Zhangsi\';-- ","1234567");
        if(user == null)
            System.out.println(false);
        else
            System.out.println(true);
    }

###输出结果

true

###使用parentStatement

        try {
            connection = DriverManager.getConnection(DB_URL,USER,PASSWORD);
//            statement = connection.createStatement();
            String sql = "SELECT * FROM user_login WHERE userName = '"+ username + "' AND password = " +password;
//            resultSet = statement.executeQuery(sql);

            sql = "SELECT * FROM user_login WHERE userName = ? AND password = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,username);
            preparedStatement.setString(2,password);

            resultSet = preparedStatement.executeQuery();
            while(resultSet.next())
            {
                user = new User();
                user.setId(resultSet.getInt("id"));
                user.setUserName(resultSet.getString("userName"));
                user.setSex(resultSet.getInt("sex"));
                user.setPassword(resultSet.getString("password"));
            }
        } catch (SQLException e)
        {
            System.out.println(e.toString());
        }
        finally {
            try{
                if(connection != null) connection.close();
                if(preparedStatement != null)preparedStatement.close();
                if(resultSet != null)resultSet.close();

            }catch (SQLException e)
            {

            }
        }

输出

false

##其他注意事项

  • 严格的数据库权限管理
    • 仅给予Web应用访问数据库的最小权限
    • 避免Drop table等权限

输入图片说明

  • 封装数据库错误
    • 禁止直接将后端数据库异常信息爆漏给用户
    • 对后端异常信息进行必要的封装,避免用户直接查看到后端异常

输入图片说明

  • 机密信息禁止明文存储
    • 涉密信息需要加密处理
    • 使用AES_ENCRYPT/AES_DECRYPT加密和解密

输入图片说明

转载于:https://my.oschina.net/hava/blog/751973

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值