緣由
最近在的一個項目上面,發現有人在給我搞 SQL 注入,我真的想說我那么點資源測試用的阿里云服務器,個人估計哈,估計能抗住他的請求。狗頭.png
系統上面的截圖
數據庫截圖
說句實在的,看到這個之后我立馬就是在想啊,現在我們都是用的成熟的ORM 框架,一般調用 ORM 框架的方法操作數據庫是不會有問題的。
后面又讓我想起來了,#{}
與 ${}
的區別,這里總結下,說一句通俗易懂的話:使用 #{}
可以防止SQL注入,使用 ${}
就會出現 SQL 注入。
代碼理解
咱們都是程序員,都喜歡說,別給我說那么多廢話,show me the code,那么我們直接看代碼把,我們通過偽代碼來理解下:
public void test(String name, String id){String sql = "update orders set name = "+name+" where id = ?";}
這里我為了方便與說明,就是使用上面的一個SQL 來解析,從上面的 SQL 可以看到兩點信息:
- name: 是直接使用了拼接字符串的方式,這里就是類似于
${}
做的事情 - id:使用了
?
做為占位符,做了一次預處理,先去編譯SQL,后面再來做參數化操作,這個是#{}
的具體原理
如果是 name 被惡意傳入了 SQL 代碼,比如:
") OR (SELECT*FROM(SELECT(SLEEP(3)))hnao) limit 1#
-- 或者
" union select 1--
那么解析出來的 SQL 就會變成:
update orders set name = ) OR (SELECT*FROM(SELECT(SLEEP(3)))hnao) limit 1update orders set name = union select 1
雖然上面比較難看,因為我這里舉例是 update,如果是查詢呢:
select name from orders union select 1--
那這樣子是不是就出問題了?其實只要他成功一次,那么就可能竊取到我們數據庫的信息了。
#{}
與 ${}
的區別
這里我也去百度了下,然后整理一下大致的區別,在 MyBatis 中,#{}
和 ${}
都是用于在 SQL 語句中插入參數的占位符,但它們之間有著顯著的區別。
-
預編譯與安全性:
#{}
:這是一個預編譯(PreparedStatement
)的占位符。當 MyBatis 在解析 XML 映射文件或注解中的 SQL 時,它會為#{}
中的參數生成一個 PreparedStatement,并使用?
作為占位符。這意味著 SQL 語句會被預編譯,并且在執行時會使用參數化查詢,這樣可以防止 SQL 注入攻擊。${}
:這是一個簡單的字符串替換。MyBatis 會直接替換${}
中的內容為 SQL 語句中的相應部分。這意味著 SQL 語句不會被預編譯,而是會動態地構建和執行。這可能會導致 SQL 注入攻擊,因為它允許不受限制的字符串替換。
-
用法:
#{}
:通常用于插入參數值,例如列值、條件值等。${}
:通常用于插入 SQL 片段,如表名、列名、動態 SQL 語句等。
-
動態 SQL:
- 在 MyBatis 中,
${}
更多地用于構建動態 SQL,因為它允許直接替換 SQL 語句中的任何部分。然而,由于這種靈活性,它也增加了 SQL 注入的風險。 #{}
在動態 SQL 中通常用于插入參數值,以確保安全性。
- 在 MyBatis 中,
-
類型處理器:
- 對于
#{}
插入的參數,MyBatis 會使用相應的類型處理器(Type Handler)來確保參數與數據庫中的列類型匹配,并進行必要的類型轉換。 - 而對于
${}
,由于它直接替換 SQL 語句中的部分,因此不會使用類型處理器。
- 對于
-
注意事項:
- 盡可能使用
#{}
來插入參數值,以確保 SQL 語句的安全性和性能。 - 如果確實需要使用
${}
(例如,插入表名或列名),請確保傳入的字符串是安全的,并且不包含任何來自不受信任的來源的內容。 - 在使用動態 SQL 時,要特別注意 SQL 注入的風險,并采取相應的預防措施。
- 盡可能使用
總之,#{}
和 ${}
在 MyBatis 中各有其用途,但 #{}
通常更安全、更可靠,并應優先使用。