【转】mybatis如何防止sql注入
sql 注入大家都不陌生,是一种常见的攻击方式,攻击者在界面的表单信息或 url 上输入一些奇怪的 sql 片段,例如“or ‘1’=’1’”这样的语句,有可能入侵参数校验不足的应用程序。所以在我们的应用中需要做一些工作,来防备这样的攻击方式。在一些安全性很高的应用中,比如银行软件,经常使用将 sql 语句全部替换为存储过程这样的方式,来防止 sql 注入,这当然是一种很安全的方式,但我们平时开发中,可能不需要这种死板的方式。
mybatis 框架作为一款半自动化的持久层框架,其 sql 语句都要我们自己来手动编写,这个时候当然需要防止 sql 注入。其实 Mybatis 的 sql 是一个具有“输入 + 输出”功能,类似于函数的结构,如下:
1 2 3 4 | <select id=“getBlogById“ resultType=“Blog“ parameterType=” int ”><br> select id,title,author,content from blog where id=#{id} </select> |
这里,parameterType标示了输入的参数类型,resultType 标示了输出的参数类型。回应上文,如果我们想防止 sql 注入,理所当然地要在输入参数上下功夫。上面代码中高亮部分即输入参数在 sql 中拼接的部分,传入参数后,打印出执行的 sql 语句,会看到 sql 是这样的:
select id,title,author,content from blog where id = ?
不管输入什么参数,打印出的 sql 都是这样的。这是因为 mybatis 启用了预编译功能,在 sql 执行前,会先将上面的 sql 发送给数据库进行编译,执行时,直接使用编译好的 sql,替换占位符“?”就可以了。因为 sql 注入只能对编译过程起作用,所以这样的方式就很好地避免了 sql 注入的问题。
mybatis 是如何做到 sql 预编译的呢?其实在框架底层,是jdbc 中的 PreparedStatement 类在起作用,PreparedStatement 是我们很熟悉的 Statement 的子类,它的对象包含了编译好的 sql 语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行一个sql 时,能够提高效率,原因是 sql 已编译好,再次执行时无需再编译。
话说回来,是否我们使用 mybatis 就一定可以防止 sql 注入呢?当然不是,请看下面的代码:
1 2 3 4 5 | <select id=“orderBlog“ resultType=“Blog“ parameterType=”map”> select id,title,author,content from blog order by ${orderParam} </select> |
仔细观察,内联参数的格式由“#{xxx}”变为了 ${xxx}。如果我们给参数“orderParam”赋值为”id”, 将 sql 打印出来,是这样的:
select id,title,author,content from blog order by id
显然,这样是无法阻止 sql 注入的。在 mybatis 中,”${xxx}”这样格式的参数会直接参与 sql 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式,所以,这样的参数需要我们在代码中手工进行处理来防止注入。
结论:在编写 mybatis 的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql 注入攻击。