sql注入问题

本文探讨了SQL注入的危害及原理,并介绍了使用PreparedStatement预防SQL注入的方法。通过案例分析,展示了如何安全地处理用户输入,确保数据库安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SELECT * FROM USER WHERE NAME='' AND PASSWORD='xxx';

-- sql注入: 请尝试以下 用户名和密码.

/* 用户名:
密码: xxx
*/
-- 将用户名和密码带入sql语句, 如下:

SELECT * FROM USER WHERE NAME='xxx' OR 1=1 --' and password='xxx';

-- 发现sql语句失去了判断效果,条件部分成为了恒等式.
-- 导致网站可以被非法登录, 以上问题就是sql注入的问题.
//-------------------------------------------------------------------------------------------
思考会出现什么问题?
将用户名密码带入sql语句,发现sql语句变成了如下形式:
SELECT * FROM t_student WHERE NAME='abcd'OR 1=1;-- ' AND PASSWORD='1234';
该sql语句就是一个 恒等条件.所以 一定会查询出记录. 造成匿名登陆.有安全隐患

如上问题,是如何解决呢?
1>解决办法:在运送sql时,我们使用的是Statement对象. 如果换成prepareStatement对象,那么就不会出现该问题.
2>sql语句不要再直接拼写.而要采用预编译的方式来做.
完成如上两步.即可解决问题.

 

 

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

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

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

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

1 String sql = "select * from goods where min_name = ?";  // 含有参数
2 PreparedStatement st = conn.prepareStatement(sql);
3 st.setString(1, "儿童'"); // 参数赋值
4 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会对参数做转义,接下来再看个例子。

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

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

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

1 String sql = "select * from goods where min_name like ?";  // 含有参数
2 st = conn.prepareStatement(sql);
3 st.setString(1, "儿童" + "%"); // 参数赋值
4 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上钻牛角尖,而是提醒读者善于思考,每天都在写着重复的代码,却从来没有停下脚步细细品味。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值