突发奇想想知道为什么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);
这个人的博客: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