什么是 SQL 注入,這些坑得避開
1、sql 注入是什么
sql 注入就是用戶通過輸入的參數,拼接到原先的 sql 中,成為 sql 的一部分,從而影響 sql 的功能和執行結果
2、sql 注入破壞力
-小兵破壞力
比如原先 sql 如下
- select * from user where name='用戶名' and password='密碼';
用戶輸入
- name:臻大蝦'-- '注釋
- password:密碼
那最終的結果猜猜是什么?
- select * from user where name='臻大蝦'-- '注釋' and password='密碼';
兩個-- 代表注釋,所以這條 sql 只需要輸入用戶名,就可以獲取用戶信息,跳過了密碼的校驗
-boss 破壞力
來個厲害的,比如用戶輸入以下參數
- name:臻大蝦
- password:'; drop table user;-- '注釋
最終的 sql:
- select * from user where name='臻大蝦' and password=''; drop table user;-- '注釋';
user 表居然被刪除了,看到這,此時的你可能想原地爆炸。
3、對策
3.1、PreparedStatement 預編譯
使用預編譯,這樣傳入的參數,會被當作字符串,也就是被引號包起來
拿剛才的例子,用戶輸入
- name:臻大蝦'-- '注釋
- password:密碼
如果使用了預編譯,那最終的 sql
- select * from user where name='臻大蝦\'-- \'注釋' and password='密碼';
參數中的引號被轉義,從而避免成為 sql 的一部分。
3.2、有些語句不能預編譯
預編譯獲取參數是根據#,而$ 是拼接的意思
#{}:解析為預編譯語句的一個參數占位符
${}:僅僅作為一個字符串,在動態 sql 中直接替換變量,傳入什么值,就是什么值
比如傳入:'臻大蝦' or 1=1
- 1、SELECT * FROM user WHERE name=#{name} //SELECT * FROM user WHERE name='\'臻大蝦\' or 1=1'
- 2、SELECT * FROM user WHERE name=${name} //SELECT * FROM user WHERE name='臻大蝦' or 1=1
但是有些情況使用#會報錯,比如 like、in 如果使用#會報錯,而使用
還有 order by,根據傳入的參數排序,這些情況就會有 sql 注入的危險,那怎么辦呢?
-
like
- select * from user where name like '%#{name}%' //會報錯
- select * from user where name like '%${name}%' //正常
正確寫法
使用 mysql 的字符串拼接函數 concat
- select * from user where name like concat('%',#{name},'%')
-
in
正確寫法,使用 foreach
- @Select("<script>" +
- "select * from user where id in "+
- "<foreach item='item' index='index' collection='userIds' open='(' separator=',' close=')'> " +
- "#{item}" +
- "</foreach>"+
- "</script>")
- List<User> getByIds(@Param("userIds") List<Long> userIds);
-
order by
有時需要根據前端傳入的參數來排序,此時就會有 sql 注入的危險,此時可以使用白名單
比如
- List<String> allowSortColumnList= Lists.newArrayList("age","score");
- if(!allowSortColumnList.contains(sortParam)){
- throw new RuntimeException("can not sort by "+sortParam);
- }