如何防止SQL注入mybatis预编译解决SQL注入原理

101 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

select内的or1= 1 这种情况 mybatis可以用#来防止(底层也是菜要预编译,使用的也是jdbc的PrepareStatement) 或者使用正则表达式筛选或者jdbc的PrepareStatement先进行预编译

那为什么它这样处理就能预防SQL注入提高安全性呢?

其实是因为SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or ‘1=1’,数据库也会作为一个参数一个字段的属性值来处理,而不会作为一个SQL指令

MyBatis预编译机制详解

一. "#{}“和”${}"的区别

"#{}"是将传入的值按照字符串的形式进行处理,如下面这条语句:

select user_id,user_name from t_user where user_id = #{user_id} MyBaits会首先对其进行预编译,将#{user_ids}替换成?占位符,然后在执行时替换成实际传入的user_id值,**并在两边加上单引号,以字符串方式处理。**下面是MyBatis执行日志:

select id, user_name from t_user where id = ?

因为"#{}"会在传入的值两端加上单引号,所以可以很大程度上防止SQL注入。有关SQL注入的知识会在后文进行说明。因此在大多数情况下,建议使用"#{}"。

"${}"是简单的字符串替换,即将传入的值直接拼接到SQL语句中,且不会自动加单引号。将上面的SQL语句改为:

select user_id,user_name from t_user where user_id = ${user_id}

再观察MyBatis的执行日志:

select id, user_name, real_name, sex, mobile, email, note, position_id from t_user where id = 1 可以看到,参数是直接替换的,且没有单引号处理,这样就有SQL注入的风险。

但是在一些特殊情况下,使用${}是更适合的方式,如表名、orderby等。见下面这个例子:

select user_id,user_name from tablenamewhereuserid={table_name} where user_id = {user_id} 这里如果想要动态处理表名,就只能使用"{}",因为如果使用"#{}", 就会在表名字段两边加上单引号,变成下面这样: select user_id,user_name from 't_user' where user_id = {user_id} 这样SQL语句就会报错。

在MyBatis中,采用"${}“是简单的字符串替换,肯定无法应对SQL注入。那么”#{}"是怎样解决SQL注入的呢?

select * from users where username=#{username} and password=md5(#{password}) 这样一来,当用户再次输入’or 1=1#,MyBatis执行SQL语句时会将其替换成: select * from users where username=''or 1=1#' and password=md5('')

由于在两端加了双引号,因此输入的内容就是一个普通字符串,其中的#注释和or 1=1都不会生效,这样就无法登陆成功了,从而有效防止了SQL注入。