SQL注入(SQL injection)是因为应用程序在执行SQL语句的时候没有正确的处理用户输入字符串,将用户输入的恶意字符串拼接到了SQL语句中执行,从而导致了SQL注入。
基于JDBC查询的SQL注入,暂不讨论基于ORM实现的框架注入,ORM
现在大部分框架都自带ORM
模块,作为脚手架必备元素。
不过多描述注入方法,只探究形成原因
SQL注入示列
Java代码片段如下:
// 获取用户传入的用户名
String user = request.getParameter("user");
// 定义最终执行的SQL语句,这里会将用户从请求中传入的host字符串拼接到最终的SQL
// 语句当中,从而导致了SQL注入漏洞。
String sql = "select host,user from mysql.user where user = '" + user + "'";
// 创建预编译对象
PreparedStatement pstt = connection.prepareStatement(sql);
// 执行SQL语句并获取返回结果对象
ResultSet rs = pstt.executeQuery();
SQL注入防御
如何去防御注入呢?
- 转义用户请求的参数值中的’(单引号)、”(双引号)。
- 限制用户传入的数据类型,如预期传入的是数字,那么使用:Integer.parseInt()/Long.parseLong等转换成整型。
- 使用PreparedStatement对象提供的SQL语句预编译。
只过滤’(单引号)或”(双引号)并不能有效的防止整型注入,但是可以有效的防御字符型注入。解决注入的根本手段应该使用参数预编译的方式。
PreparedStatement SQL预编译查询
将上面存在注入的Java代码改为?(问号)占位的方式即可实现SQL预编译查询。
// 获取用户传入的用户ID
String id = request.getParameter("id");
// 定义最终执行的SQL语句,这里会将用户从请求中传入的host字符串拼接到最终的SQL
// 语句当中,从而导致了SQL注入漏洞。
String sql = "select id, username, email from sys_user where id =? ";
// 创建预编译对象
PreparedStatement pstt = connection.prepareStatement(sql);
// 设置预编译查询的第一个参数值
pstt.setObject(1, id);
// 执行SQL语句并获取返回结果对象
ResultSet rs = pstt.executeQuery();
值得注意的是并不是使用PreparedStatement来执行SQL语句就没有注入漏洞,而是将用户传入部分使用?(问号)占位符表示并使用PreparedStatement预编译SQL语句才能够防止注入!