以mybatis为例浅聊SQL注入
以用户登录为例,传统代码为一个form表单,输入username和password去数据库发送sql检索,用的是字符串拼接${}。
select * from t_user where password = ‘ password′andusername=′ {username}’
如果页面输入
zhangsan
1111’ or ‘1 = 1
则sql语句为
select * from t_user where password = ‘1111’ or ‘1 = 1’and username = ‘zhangsan’
此时无论用户名为zhangsan的该用户密码为何值,均能查询出一条记录。
而用了预编译的#{}占位符后
代码为
select * from t_user where password = #{password} and username = #{username}
即mybatis发送给数据库的sql为
select * from t_user where password = ? and username = ?
在数据库端便会对该sql进行编译,如果下次有该查询时,mabatis会发送select * from t_user where password = #{password} and username = #{username}作为“KEY”,页面传入的参数作为“VALUE”
去数据库查询,预编译的方式使得发送至数据库的值为sql “KEY” 和 参数 “VALUE”,
数据库根据KEY找到编译好的sql,将参数注入进行查询
如果页面输入
zhangsan
1111’ or ‘1 = 1
则sql语句为
select * from t_user where password = ‘1111’ or ‘1 = 1’and username = ‘zhangsan’
看到这也许会有疑问,这和传统的字符串拼接,得到的sql还是一样的啊?
其实并不是,sql只是数据库端给我们看的一段字符串,真正的查询方式,
传统字符串拼接查询,传入数据库的是一个参数,为在程序端拼接好的sql,直接去数据库执行。
而预编译方式,传入数据库的是两个参数,一个是select * from t_user where password = ? and username = ?语句,一个是值。如果数据库里没该条SQL就会编译好缓存,如果缓存有的话,直接类似“函数接收参数”的方式去执行sql
,那么,数据库里是不会有任何一条记录的密码为1111’ or ‘1 = 1,就不会返回记录。从某种意义上来说防止了SQL注入。