PreparedStatement防止SQL注入小记

突发奇想想知道为什么preparedstatement.setString()这种设置参数为什么能防止SQL注入,它这么设置后打印出来的SQL语句是什么格式的,

select count(*) as total from test0 t  where t.name = '"+username+"'"  这种如果输入  ' or 1=1 -- (PS: --空格, 这里必须要有空格才算注释)
这种会变成  select count(*) as total from test0 t where name = ' ' or 1=1 这种语句,可以恒为真,
那么下面这种代码会变成什么样的查询SQL?

sb.append(" select count(*) as total from test0 t  where t.name = ?  ");
ps = conn.prepareStatement(sb.toString());
ps.setString(1,username);

看了些其他人的文章都是说什么预编译,然后SQL结构固定,只是然后设置了参数,SQL的逻辑不变,但是如何做到SQL逻辑不变的?

这个人的博客:http://blog.csdn.net/lisehouniao/article/details/51523497 中写道:“程序会对该条sql首先进行预编译,然后会将传入的字符串参数以字符串的形式去处理,即会在参数的两边自动加上单引号(’param’),而Statement则是直接简单粗暴地通过人工的字符串拼接的方式去写sql,那这样就很容易被sql注入。 

如果是加上单引号那么查询语句就变成了:

select count(*) as total from test0 t where t.name =  ' ' or 1=1  (ps: 注释掉的我就不写了,免得干扰)

这个语句和上面是无差的,只有自己看代码(其实这个连接的博主后面也写了,但是没看到最后)  ,后来发现preparedstatement这个类中的setString()方法会做如下几件事:

1. 将参数加上单引号

2. 对参数中的\0 ,\r ,\n ,\' 等字符会做转义

如此SQL语句变为:

select count(*) as total from test0 where t.name = '  \' or 1=1-- '  查询出数据为0

这样 ' or 1=1 就作为一个参数,查询逻辑是没有被改变的


备注如果select count(*) as total from test0 t  where t.name = '?' 采用设置参数方式,同时给问号还加入了单引号那么SQL语句就变成如下:

select count(*) as total from test0 t where t.name = ' 'name ' ' 这种语句是要报错的,不符合语法

StringBuilder sb = new StringBuilder();
//sb.append(" select count(*) as total from test0 t  where t.name = '"+username+"'");
sb.append(" select count(*) as total from test0 t  where t.name = '?' ");
System.out.println(sb.toString());
ps = conn.prepareStatement(sb.toString());
ps.setString(1,username);
ResultSet rs = ps.executeQuery();

如果想看preparedstatement设置参数后打印出的SQL语句可以使用参考如下代码:

ps = conn.prepareStatement(sb.toString());
ps.setString(1,username);
System.out.println(ps.toString());
打印结果如下: com.mysql.jdbc.JDBC4PreparedStatement@70da8742:   select count(*) as total from test0 t  where t.name = '\' or 1=1 -- ' 

参考博客: http://blog.csdn.net/lisehouniao/article/details/51523497

你可能感兴趣的:(注意事项)