mybatis 防止sql注入的注意事項
1.sql注入的原理
在頁面引數輸入時,輸入有語法含義的字串,這樣可以改變你原有的SQL。
例如:
- <?php
- $username = "aaa";
- $pwd = "pwd";
- $sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'";
- echo $sql; //輸出 SELECT * FROM table WHERE username = 'aaa' AND pwd = 'pwd'
- ?>
惡意使用者 在不知道密碼的情況下,通過or關鍵字來sql注入。
- <?php
- $username = "aaa";
- $pwd = "fdsafda' or '1'='1"; //前面的密碼是瞎填的..後來用or關鍵字..意思就是無所謂密碼什麼都執行
- $sql = "SELECT * FROM table WHERE username = '{$username}' AND pwd = '{$pwd}'";
- echo $sql; //輸出 SELECT * FROM table WHERE username = 'aaa' AND pwd = 'fdsafda' or '1'='1'
- ?>
2.防止sql的原理
將sql語句的執行分成兩部分
第一步:先將不帶引數的sql進行發給資料庫進行預編譯。
第二步:將引數和預編譯好的sql發給資料庫執行。
因為:sql注入只會影響到,sql預編譯階段,執行時就不管引數中的語法關鍵字了。
注意:MyBatis是如何做到SQL預編譯的呢?其實在框架底層,是JDBC中的PreparedStatement類在起作用,PreparedStatement是我們很熟悉的Statement的子類,它的物件包含了編譯好的SQL語句。這種“準備好”的方式不僅能提高安全性,而且在多次執行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執行時無需再編譯。
例如:
<select id="getBlogById" resultType="Blog" parameterType=”int”>
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注入的問題。
但是要注意一個細節
#{}:相當於JDBC中的PreparedStatement
${}:是輸出變數的值,沒有防止sql注入,需要自己通過技術手段過濾引數來防止sql注入。
例如:同樣的sql語句只要把 #{id} 變成${id}就不能防止sql注入了。
<select id="getBlogById" resultType="Blog" parameterType=”int”>
SELECT id,title,author,content
FROM blog
WHERE id=${id}
</select>
打印出執行的SQL語句,會看到SQL是這樣的:
SELECT id,title,author,content FROM blog WHERE id = 3
引數和語句一起編譯的不能,沒有采用jdbc的引數繫結方式。