博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据库开发 - SQL注入与防范
阅读量:5845 次
发布时间:2019-06-18

本文共 4744 字,大约阅读时间需要 15 分钟。

  hot3.png

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

携程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

你可能感兴趣的文章
〔转〕Word域的应用和详解2_等式和公式域
查看>>
FZU 1502 Letter Deletion
查看>>
javascript写的ajax请求
查看>>
寄存器是什么 有什么作用
查看>>
转载 《Python爬虫学习系列教程》学习笔记
查看>>
Flex组件的项目渲染器(ItemRenderer)使用总结
查看>>
python - 列表
查看>>
NGUI的输入框制作(attach- input filed script的使用)
查看>>
[异常笔记] zookeeper集群启动异常: Cannot open channel to 2 at election address ……
查看>>
mysql 03
查看>>
windows系统下搭建私有nuget仓储服务器, 打包程序集并推送到私有nuget仓储服务器...
查看>>
NgDL:第三周:浅层NN
查看>>
OpenCV基于傅里叶变换进行文本的旋转校正
查看>>
谁分配、谁释放的原则需要goto
查看>>
C#中字符串的内存分配与驻留池
查看>>
PIX防火墙配置DHCP
查看>>
Centreon 安装部署指南
查看>>
利用ADMT进行Exchange跨域迁移之三:迁移Exchange用户邮箱
查看>>
linux中生成考核用的NTFS文件系统(历史版本)
查看>>
项目管理修炼之道之规划项目
查看>>