先感慨下,好久没写博客了,一是工作太忙,二是身体不太给力,好在终于查清病因了,趁着今天闲下来,迫不及待与读者交流,最后忠告一句:身体是活着的本钱!

言归正传,对java有了解的同学基本上都体验过JDBC,基本都了解PreparedStatement,PreparedStatement相比Statement基本解决了SQL注入问题,而且效率也有一定提升。

关于PreparedStatement和Statement其他细节我们不讨论,只关心注入问题。无论读者是老鸟还是菜鸟,都需要问一下自己,PreparedStatement真的百分百防注入吗?

接下来我们研究一下PreparedStatement如何防止注入,本文以MySQL数据库为例。

为了避免篇幅过长,我这里只贴代码片段,希望读者能有一定的基础。

 String sql = "select * from goods where min_name = ?";  // 含有参数
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1, "儿童"); // 参数赋值
System.out.println(st.toString()); //com.mysql.jdbc.JDBC4PreparedStatement@d704f0: select * from goods where min_name = '儿童'

这段代码属于JDBC常识了,就是简单的根据参数查询,看不出什么端倪,但假如有人使坏,想注入一下呢?

 String sql = "select * from goods where min_name = ?";  // 含有参数
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1, "儿童'"); // 参数赋值
System.out.println(st.toString()); //com.mysql.jdbc.JDBC4PreparedStatement@d704f0: select * from goods where min_name = '儿童\''

简单的在参数后边加一个单引号,就可以快速判断是否可以进行SQL注入,这个百试百灵,如果有漏洞的话,一般会报错。

之所以PreparedStatement能防止注入,是因为它把单引号转义了,变成了\',这样一来,就无法截断SQL语句,进而无法拼接SQL语句,基本上没有办法注入了。

所以,如果不用PreparedStatement,又想防止注入,最简单粗暴的办法就是过滤单引号,过滤之后,单纯从SQL的角度,无法进行任何注入。

其实,刚刚我们提到的是String参数类型的注入,大多数注入,还是发生在数值类型上,幸运的是PreparedStatement为我们提供了st.setInt(1, 999);这种数值参数赋值API,基本就避免了注入,因为如果用户输入的不是数值类型,类型转换的时候就报错了。

好,现在读者已经了解PreparedStatement会对参数做转义,接下来再看个例子。

 String sql = "select * from goods where min_name = ?";  // 含有参数
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1, "儿童%"); // 参数赋值
System.out.println(st.toString()); //com.mysql.jdbc.JDBC4PreparedStatement@8543aa: select * from goods where min_name = '儿童%'

我们尝试输入了一个百分号,发现PreparedStatement竟然没有转义,百分号恰好是like查询的通配符。

正常情况下,like查询是这么写的:

 String sql = "select * from goods where min_name like ?";  // 含有参数
st = conn.prepareStatement(sql);
st.setString(1, "儿童" + "%"); // 参数赋值
System.out.println(st.toString()); //com.mysql.jdbc.JDBC4PreparedStatement@8543aa: select * from goods where min_name like '儿童%'

查询min_name字段以"儿童"开头的所有记录,其中"儿童"二字是用户输入的查询条件,百分号是我们自己加的,怎么可能让用户输入百分号嘛!等等!如果用户非常聪明,偏要输入百分号呢?

String sql = "select * from goods where min_name like ?";  // 含有参数
st = conn.prepareStatement(sql);
st.setString(1, "%儿童%" + "%"); // 参数赋值
System.out.println(st.toString()); //com.mysql.jdbc.JDBC4PreparedStatement@8543aa: select * from goods where min_name like '%儿童%%'

聪明的用户直接输入了"%儿童%",整个查询的意思就变了,变成包含查询。实际上不用这么麻烦,用户什么都不输入,或者只输入一个%,都可以改变原意。

虽然此种SQL注入危害不大,但这种查询会耗尽系统资源,从而演化成拒绝服务攻击。

那如何防范呢?笔者能想到的方案如下:

          ·直接拼接SQL语句,然后自己实现所有的转义操作。这种方法比较麻烦,而且很可能没有PreparedStatement做的好,造成其他更大的漏洞,不推荐。

          ·直接简单暴力的过滤掉%。笔者觉得这方案不错,如果没有严格的限制,随便用户怎么输入,既然有限制了,就干脆严格一些,干脆不让用户搜索%,推荐。

目前做搜索,只要不是太差的公司,一般都有自己的搜索引擎(例如著名的java开源搜索引擎solr),很少有在数据库中直接like的,笔者并不是想在like上钻牛角尖,而是提醒读者善于思考,每天都在写着重复的代码,却从来没有停下脚步细细品味。

有读者可能会问,为什么我们不能手动转义一下用户输入的%,其他的再交给PreparedStatement转义?这个留作思考题,动手试一下就知道为什么了。

注意,JDBC只是java定义的规范,可以理解成接口,每种数据库必须有自己的实现,实现之后一般叫做数据库驱动,本文所涉及的PreparedStatement,是由MySQL实现的,并不是JDK实现的默认行为,也就是说,不同的数据库表现不同,不能一概而论。

用java PreparedStatement就不用担心sql注入了吗?的更多相关文章

  1. 【转载】以Java的视角来聊聊SQL注入

    以Java的视角来聊聊SQL注入 原创 2017-08-08 javatiku Java面试那些事儿 在大二就接触过sql注入,之前一直在学习windows逆向技术,认为web安全以后不是自己的从业方 ...

  2. 重新学习MySQL数据库11:以Java的视角来聊聊SQL注入

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  3. PreparedStatement是如何防止SQL注入的?

    为什么在Java中PreparedStatement能够有效防止SQL注入?这可能是每个Java程序员思考过的问题. 首先我们来看下直观的现象(注:需要提前打开mysql的SQL文日志) 1. 不使用 ...

  4. jdbc java数据库连接 8)防止sql注入

    回顾下之前jdbc的开发步骤: 1:建项目,引入数据库驱动包 2:加载驱动 Class.forName(..); 3:获取连接对象 4:创建执行sql语句的stmt对象;  写sql 5:执行sql ...

  5. java学习笔记38(sql注入攻击及解决方法)

    上一篇我们写了jdbc工具类:JDBCUtils ,在这里我们使用该工具类来连接数据库, 在之前我们使用 Statement接口下的executeQuery(sql)方法来执行搜索语句,但是这个接口并 ...

  6. 转!! PreparedStatement是如何防止SQL注入的

    SQL注入最简单也是最常见的例子就是用户登陆这一模块,如果用户对SQL有一定的了解,同时系统并没有做防止SQL注入处理,用户可以在输入的时候加上'两个冒号作为特殊字符,这样的话会让计算机认为他输入的是 ...

  7. XSS过滤JAVA过滤器filter 防止常见SQL注入

    Java项目中XSS过滤器的使用方法. 简单介绍: XSS : 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩 ...

  8. preparedstatement 为什么可以防止sql注入

    有大神总结的很好,,参考文献 http://www.importnew.com/5006.html preparedstatement优势:sql的预编译(数据库层面完成)提升效率. 为什么可以防止s ...

  9. Java应用开发中的SQL注入攻击

    1. 什么是SQL注入攻击? SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员越来越多.但是由于程序员的水平及经验参差不齐,相当一部分 ...

随机推荐

  1. XOR Swap

    swap(a, b): a ^= b b ^= a a ^= b 先明确一下,a ^ a = 0,同时对于一切数x ^ 0 = x 可以这样理解,第三行: b ^= a b ^= a ^ b b = ...

  2. 【Hibernate框架】关联映射(多对多关联映射)

    按着我们的总结行进计划,接下来,就是有关于多对多映射的总结了. 我们来举个例子啊,很长时间以来,房价暴涨不落,但是还有很多人拥有很多套房产,假如说,一个富豪拥有九套房产,家里人么准去住哪一套,我们就以 ...

  3. DOM事件

    在慕课网上学习了DOM事件探秘课程,特此整理了一下笔记. 慕课网DOM事件探秘课程地址:http://www.imooc.com/learn/138 事件 是文档或浏览器窗口中发生的特定的交互瞬间.[ ...

  4. 【CentOS】LNMP

    本文为博主JerryChan所有,如需转载,请联系博主747618706@qq.com,并附上博客链接/////////////////目录//////////////////一.LNMP的安装 1. ...

  5. java网络编程2

    在通信双方中,ServerSocket是服务器端负责接收的一方,它负责监听指定端口,其构造函数如下: 1.ServerSocket()  throws IOException;无参构造函数,之所以存在 ...

  6. python常用模块json、os、sys

    一.序列化 json & pickle 模块 json--用于字符串和Python数据类型间进行转换 pickle---用于python特有的类型和Python的数据类型间进行转换 json: ...

  7. android事件处理之基于监听

    Android提供了了两种事件处理方式:基于回调和基于监听. 基于监听: 监听涉及事件源,事件,事件监听器.用注册监听器的方法将某个监听器注册到事件源上,就可以对发生在事件源上的时间进行监听. 最简单 ...

  8. 案例1.用Ajax实现用户名的校验

    用Ajax实现用户名的校验 java的验证类 public class UserDao { public boolean checkUserName(String name) { //这里的name是 ...

  9. fatal error

    1.   fatal error C1083: 无法打开源文件 编译报此错误:  1>c1xx : fatal error C1083: 无法打开源文件:“Projects\XXXCCCC\VB ...

  10. Codeforces Round #384 (Div. 2)D-Chloe and pleasant prizes

    D. Chloe and pleasant prizes time limit per test 2 seconds memory limit per test 256 megabytes input ...