SQL注入(SQL injection)是因为应用程序在执行SQL语句的时候没有正确的处理用户输入字符串,将用户输入的恶意字符串拼接到了SQL语句中执行,从而导致了SQL注入。

基于JDBC查询的SQL注入,暂不讨论基于ORM实现的框架注入,ORM现在大部分框架都自带ORM模块,作为脚手架必备元素。

不过多描述注入方法,只探究形成原因

SQL注入示列

Java代码片段如下:

  1. // 获取用户传入的用户名
  2. String user = request.getParameter("user");
  3. // 定义最终执行的SQL语句,这里会将用户从请求中传入的host字符串拼接到最终的SQL
  4. // 语句当中,从而导致了SQL注入漏洞。
  5. String sql = "select host,user from mysql.user where user = '" + user + "'";
  6. // 创建预编译对象
  7. PreparedStatement pstt = connection.prepareStatement(sql);
  8. // 执行SQL语句并获取返回结果对象
  9. ResultSet rs = pstt.executeQuery();

可以看到SQL注入其实主要就是参数拼接。

SQL注入防御

如何去防御注入呢?

  1. 转义用户请求的参数值中的’(单引号)、”(双引号)。
  2. 限制用户传入的数据类型,如预期传入的是数字,那么使用:Integer.parseInt()/Long.parseLong等转换成整型。
  3. 使用PreparedStatement对象提供的SQL语句预编译。

只过滤’(单引号)或”(双引号)并不能有效的防止整型注入,但是可以有效的防御字符型注入。解决注入的根本手段应该使用参数预编译的方式。

PreparedStatement SQL预编译查询

将上面存在注入的Java代码改为?(问号)占位的方式即可实现SQL预编译查询。

  1. // 获取用户传入的用户ID
  2. String id = request.getParameter("id");
  3. // 定义最终执行的SQL语句,这里会将用户从请求中传入的host字符串拼接到最终的SQL
  4. // 语句当中,从而导致了SQL注入漏洞。
  5. String sql = "select id, username, email from sys_user where id =? ";
  6. // 创建预编译对象
  7. PreparedStatement pstt = connection.prepareStatement(sql);
  8. // 设置预编译查询的第一个参数值
  9. pstt.setObject(1, id);
  10. // 执行SQL语句并获取返回结果对象
  11. ResultSet rs = pstt.executeQuery();

值得注意的是并不是使用PreparedStatement来执行SQL语句就没有注入漏洞,而是将用户传入部分使用?(问号)占位符表示并使用PreparedStatement预编译SQL语句才能够防止注入!