小伙伴想精準查找自己想看的MySQL文章?喏 → MySQL江湖路 | 專欄目錄
??我相信90%以上的同學們在平時開發時,或多或少都被隱式轉換(CONVERT_IMPLICIT)坑過,甚至測出bug前你都渾然不知,你還別不信,“無形之刃,最為致命!”
這不,我們公司僑總今天又出來現眼了~

大家好,我是陳哈哈的同事“僑總”,領導一般不敢喊我名字,都叫我小僑~
mysql> SELECT * from t_user;
+----+-----------+----------+
| id | username | password |
+----+-----------+----------+
| 1 | 陳哈哈 | abcd1234 |
| 2 | 僑布斯 | 1234 |
| 3 | 提莫 | 1234abcd |
+----+-----------+----------+
3 rows in set (0.00 sec)
??不能展示真實資料,見諒~~ 上面是這張用戶表的原始資料,僑總用下面的SQL查詢自己這行資料,大家先看看有沒有問題?
SELECT * from t_user where `password`=1234;
感覺沒有明顯的毛病?來看看結果:
mysql> SELECT * from t_user where `password`=1234;
+----+-----------+----------+
| id | username | password |
+----+-----------+----------+
| 2 | 僑布斯 | 1234 |
| 3 | 提莫 | 1234abcd |
+----+-----------+----------+
2 rows in set, 1 warning (0.00 sec)
?????這怎么把提莫隊長給打現行了?僑總看著他寫的SQL,像小女生般羞澀的的笑出了聲,好像內褲的顏色被大家們看到了一樣~

??好了,其實這算是MySQL給開發者留下的不友好的禮物(坑),今天我們一起從以下三個角度去聊一聊MySQL的隱式轉換,
??來不及了,快上車!
索引
- 1、SQL陳述句中隱式轉換的坑
- 2、黑客同學喜歡用隱式轉換進行SQL注入攻擊
- 3、索引中隱式轉換的坑
- 總結
- 附、一張有故事的照片(十七)
1、SQL陳述句中隱式轉換的坑
先看一下官方的隱試轉換說明:

翻譯成人話:
- 兩個引數至少有一個是 NULL 時,比較的結果也是 NULL,例外是使用 <=> 對兩個 NULL 做比較時會回傳 1,這兩種情況都不需要做型別轉換,
- 兩個引數都是字串,會按照字串來比較,不做型別轉換
- 兩個引數都是整數,按照整數來比較,不做型別轉換
- 十六進制的值和非數字做比較時,會被當做二進制串
- 有一個引數是 TIMESTAMP 或 DATETIME,并且另外一個引數是常量,常量會被轉換為 timestamp
- 有一個引數是 decimal 型別,如果另外一個引數是 decimal 或者整數,會將整數轉換為 decimal 后進行比較,如果另外一個引數是浮點數,則會把 decimal 轉換為浮點數進行比較
所有其他情況下,兩個引數都會被轉換為浮點數再進行比較,(這里所說的浮點數一般默認為double型別)
??可以看到,非前六種以外的型別轉換都要轉成浮點型別來處理,這意味著什么?意味著MySQL承認了隱式轉換這個事兒,還表示不愛看官方檔案的哥們兒出問題活該~~
??我們用一些具體示例來看一下,通過下述SQL可見,當1234沒有引號也就是整數時,‘1234abcd’ = 1234 → true,說明MySQL對’1234abcd’做了轉型,轉成了浮點型別,結果是:1234abcd => 1234
# 0:false;1:true
mysql> SELECT '1234abcd' = '1234';
+---------------------+
| '1234abcd' = '1234' |
+---------------------+
| 0 |
+---------------------+
1 row in set (0.00 sec)
# 0:false;1:true
mysql> SELECT '1234abcd' = 1234;
+-------------------+
| '1234abcd' = 1234 |
+-------------------+
| 1 |
+-------------------+
1 row in set, 1 warning (0.00 sec)
??為啥1234abcd => 1234呢? 其實’1234’和’abcd’都會轉成浮點數,即:1234+0=1234,非數字型別的都被直接轉成了 0
mysql> SELECT '1234' + 'abcd';
+-----------------+
| '1234' + 'abcd' |
+-----------------+
| 1234 |
+-----------------+
1 row in set, 1 warning (0.00 sec)
??你發現了什么?原來字串涉及到 +、=、-、/ 等等運算子時都會進行隱式轉型,也就是轉成double,那么字串轉double是怎么轉的呢?
# 轉成:'1aaaa' = 1
mysql> SELECT '1aaaa' = 1;
+-------------+
| '1aaaa' = 1 |
+-------------+
| 1 |
+-------------+
1 row in set, 1 warning (0.00 sec)
# 轉成:'a1111' = 0
mysql> SELECT 'a1111' = 1;
+-------------+
| 'a1111' = 1 |
+-------------+
| 0 |
+-------------+
1 row in set, 1 warning (0.00 sec)
# 轉成:0 + 0 =0
mysql> SELECT 'aa' + 'aa' = 1;
+-----------------+
| 'aa' + 'aa' = 1 |
+-----------------+
| 0 |
+-----------------+
1 row in set, 2 warnings (0.00 sec)
# 轉成:0 + 0 + 1 =1
mysql> SELECT 'aa' + 'aa' + '1' = 1;
+-----------------------+
| 'aa' + 'aa' + '1' = 1 |
+-----------------------+
| 1 |
+-----------------------+
1 row in set, 2 warnings (0.00 sec)
??可見,是以字串從左向右取值的,且從非數字起后面的值都被轉成 0,如a11111,第一位為a,則整體轉為 0;1aaaa第一位為1,第二位為a,從第二位往后轉成0,得a11111 → 1
mysql> SELECT * from t_user where `password`=1234;
+----+-----------+----------+
| id | username | password |
+----+-----------+----------+
| 2 | 僑布斯 | 1234 |
| 3 | 提莫 | 1234abcd |
+----+-----------+----------+
2 rows in set, 1 warning (0.00 sec)
??現在我們就明白為什么能匹配到提莫了,因為在不同型別轉換時"1234abcd"被轉成了浮點型別,"abcd"轉成浮點型后為0,因此MySQL判為:“1234abcd” = ‘1234’ + 0 ,
2、黑客同學喜歡用隱式轉換進行SQL注入攻擊
通過第一部分隱式轉換的了解,我們可以預測一些簡單SQL注入的方式:
mysql> SELECT * from t_user where username='陳哈哈' and `password`=0;
+----+-----------+----------+
| id | username | password |
+----+-----------+----------+
| 1 | 陳哈哈 | abcd1234 |
+----+-----------+----------+
1 row in set, 1 warning (0.00 sec)
果然,我賬號的密碼被毫無意外的攻破了,,,想想賬號被陌生人登錄,保存多年的情書也會被泄露出去~~

如果有些朋友公司的網站用的是下面的寫法:
select * from t_user where username=${username} and `password`=${password};
??不然有些小伙伴友好的把請求引數構建成 username → a' OR 1='1 ,那么password是啥都無所謂了,是吧,
懂我意思吧,快改了,
??當然,其實很多注入攻擊的真實目的,并不是用來破解用戶賬號的,而是破壞服務器,一般我們在頁面F12發現有問題的介面后,通過腳本模擬請求引數(構造注入引數),去不斷嘗試自定義構造limit、order、where等條件,或許花不了多久就能通過一個不規范的請求入口,檢索出該表甚至其他大表全量資訊,導致公司服務器負載例外,連接數打滿,CPU200%等有趣的情況,有興趣的同學可以花幾小時嘗試破解自己公司的web~~
3、索引中隱式轉換的坑
??同理,在MySQL根據索引進行查詢時,如果你的username欄位有索引且為varchar型別,且查詢如下時:
select * from t_user where username=123;
該SQL會出現兩個問題:
1、索引失效
??無法使用到索引查詢,因為mysql會在引擎層進行型別隱式轉換(CONVERT_IMPLICIT),會先把username隱式轉換成浮點數,然后再跟你的123進行比較,然而你的索引是建在username上的,并不是在轉換后的username上的,所以進行轉換后的username相當于沒有索引,會全表掃描,換做大表中,無法使用索引,你懂得,
2、查詢結果不準確
??第一部分我們已經舉例說明,MySQL在隱式轉換時的varchar轉double,會出現很多意想不到的情況,比如 “123”," 123","123a"都會轉成123,實際場景中都是不允許出現的,
總結
??好了,MySQL的隱式轉換就說到這,點到為止,這也是我最近突然自測出的bug,我們公司測驗竟然毫不知情!真是為它感到羞愧~~ 真沒想到會有這個坑,希望這篇文章你讓你能避免它!
??一個野雞大學非科班程式員,北漂五年,還有夢想,依舊很遠,一起,加油兄弟們!
附、一張有故事的照片(十七)

這是一位網友周末在家拍到的
其實,一個對所有人都很外向的人,也不一定是真的外向,
或許,對他來說,一個人在家才是最好的禮物,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/271945.html
標籤:其他
上一篇:愛你所愛,行你所行,聽從你心
