Mysql

我可以配置 MySQL 的類型轉換以考慮 0 != ‘foo’ 嗎?

  • March 8, 2013

這篇文章指出了 MySQL 的以下安全問題:

USERS
id | email            | password_reset_token
1  | one@example.com  | QTlXww)uV!Hdg3U6aGwKV2FPvAqzVgPx
2  | two@example.com  | CZor5t7WbX#LTeqiG3v@6f3@z#)BfK*n

在這裡,我們有很好的隨機令牌,使用者必須證明他們擁有才能重置他們的帳戶。但是使用者設法送出了一個重置令牌0,所以我們執行這個查詢:

SELECT * FROM `users` WHERE `email` = 'one@example.com' AND `password_reset_token` = 0

MySQL將 token 轉換VARCHAR為 anINT以便進行比較。它認為VARCHAR不以數字開頭的 a 等於 0。因此,攻擊者可以匹配任何非數字字元串並接管帳戶

即使重置令牌開始一些數字,情況也好不到哪裡去。MySQL 在進行此比較時會忽略除初始數字之外的所有字元,因此它認為12blahblah3blah4等於12,這使得猜測變得更加容易。

**我可以將 MySQL 配置為不進行這種類型轉換嗎?**例如,如果它施法INTVARCHAR不是反之,則這種攻擊將不起作用。

筆記

如果使用'0'而不是執行查詢0,則此方法不起作用。本文根據 Ruby on Rails 對 XML 的接受度來討論這個漏洞,其中一個type=integer屬性說服 Rails 在查詢中發送一個實際的整數。

該錯誤已在 Rails 中修復;它現在將所有請求參數轉換為字元串,因此它永遠不會使用整數建構查詢。但我仍然認為 MySQL 應該是可配置的以防止這種情況。

但是,問題是您提供了一個未引用的數值,因此 MySQL 嘗試將比較的另一側轉換為 a DOUBLE,以將其與您提供的數字參數進行比較……這會給您留下 0這相當於 0。

mysql> select 'foo' = 0;
+-----------+
| 'foo' = 0 |
+-----------+
|         1 |
+-----------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------+
| Level   | Code | Message                                 |
+---------+------+-----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'foo' |
+---------+------+-----------------------------------------+
1 row in set (0.00 sec)

如果您在字元串上下文中提供 0,則計算結果為 false。

mysql> select 'foo' = '0';
+-------------+
| 'foo' = '0' |
+-------------+
|           0 |
+-------------+
1 row in set (0.00 sec)

因此,您可以用來避免這種行為的一個選項是將您的輸入值轉換為CHAR…,在大多數情況下,它已經是…但如果不是,它會按照您的預期進行評估。

mysql> select 'foo' = cast(0 as char);
+-------------------------+
| 'foo' = cast(0 as char) |
+-------------------------+
|                       0 |
+-------------------------+
1 row in set (0.00 sec)

更新,更全面地回答這個問題:

我可以將 MySQL 配置為不進行這種類型轉換嗎?

不,似乎沒有任何方法可以通過配置來避免這種情況 - 只能通過將您的潛在數字字元串顯式轉換為字元串,您說過無法完成,因為您是使用 ORM。如果存在類似的情況,最有可能的地方是SQL Server Mode,並且在比較時,這些選項都不會修改此行為。

在Bug #63112中有一些關於隱式轉換的討論:

選擇橙色>香蕉;

這是對還是錯?好吧..通常香蕉比橙子長。因此,如果長度是標準,則上述陳述是錯誤的。但大多數情況下,橙子比香蕉重,所以如果以重量為標準,上述陳述是正確的。

在比較不同的類型(橙色與香蕉,字元串與整數)時,必須使用一些“標準”或“規則”。當 MySQL 比較不同的數據類型時,它們是

$$ both $$在比較之前在內部強制轉換為 DOUBLE。整數“1”轉換為雙“1” - 字元串“a”或“UUID()”轉換為雙“0”。 $$ … $$

請注意,此錯誤報告的範圍並不嚴格限於在上下文中隱式轉換,UUID()這裡的提及有點分散注意力,因為UUID()可以返回以數字開頭的字元串並轉換為其他數字……但我認為它確實可以確認沒有替代退出,因為除了我們已經知道的“如果您想避免強制轉換,請不要使用不同的類型進行比較”之外沒有任何內容。它繼續:

為了避免強制轉換為DOUBLE內部確保比較的值是相同的數據類型。在您的情況下,id列是字元串,並且您比較的值也應該是字元串(不是 1 而是 ‘1’ - 不是 2 而是 ‘2’ 等)或在您的語句中使用cast()/convert()

$$ … $$

關於你的建議,如果要進行隱式轉換,那麼數字應該變成一個字元串而不是字元串變成一個數字……我可以看到你的觀點,在一定程度上,對於嚴格的相等比較……但是有很多除了使用該邏輯之外的其他運算符=,出現了一系列全新的問題……

mysql> SELECT CAST(100 AS CHAR) < '2';
+-------------------------+
| CAST(100 AS CHAR) < '2' |
+-------------------------+
|                       1 |
+-------------------------+
1 row in set (0.00 sec)

正確的!在字母意義上,字元串“100”“小於”字元串“2”……就像字元串“AXX”“小於”字元串“B”一樣。所以這個選擇肯定有它的缺點。

然而,還有第三種選擇,這似乎是最準確的邏輯(儘管我歡迎建設性的反對意見):如果以非數字字元開頭的字元串被轉換為DOUBLE(或任何其他數字類型,就此而言) ),該演員表的最正確結果似乎是NULL

不幸的是,這不是 MySQL 的方法。似乎這可以在源中修復,可能在sql/field.ccmy_strntod() 返回的“0”在發生錯誤時拋出警告後似乎只是簡單地返回給呼叫者,但在源中嘗試這樣的黑客攻擊超出了我的專業範圍。

引用自:https://dba.stackexchange.com/questions/36078