2012-08-11 48 views
3

現在SQL參數化是一個熱門話題,而且對於a good reason,但它是否真正做了除了正常逃脫之外的任何事情?SQL參數化:這是如何在幕後工作的?

我能想象一個參數引擎只要確保數據將其插入到查詢字符串之前體面逃過一劫,但事實是否真的它所做的一切?在連接中做不同的事情會更有意義,例如,像這樣:

> Sent data. Formatting: length + space + payload 
< Received data 
----- 
> 69 SELECT * FROM `users` WHERE `username` LIKE ? AND `creation_date` > ? 
< Ok. Send parameter 1. 
> 4 joe% 
< Ok. Send parameter 2. 
> 1 0 
< Ok. Query result: [...] 

這樣只會消除SQL注入的問題,所以你不會有避免他們通過逃逸。我能想到的參數是如何發揮作用的唯一辦法了,是逃避參數:

// $params would usually be an argument, not in the code like this 
$params = ['joe%', 0]; 

// Escape the values 
foreach ($params as $key=>$value) 
    $params[$key] = mysql_real_escape_string($value); 

// Foreach questionmark in the $query_string (another argument of the function), 
// replace it with the escaped value. 
$n = 0; 
while ($pos = strpos($query_string, "?") !== false && $n < count($params)) { 
    // If it's numeric, don't use quotes around it. 
    $param = is_numeric($params[$n]) ? $params[$n] : "'" . $params[$n] . "'"; 
    // Update the query string with the replaced question mark 
    $query_string = substr($query_string, 0, $pos) //or $pos-1? It's pseudocode... 
        . $param 
        . substr($query_string, $pos + 1); 
    $n++; 

如果是後者的話,我不會切換我的網站的參數,只是還沒有。它沒有任何優勢,我可以看到,這只是另一個強大的與弱的變量分類討論。強大的輸入可能會在編譯時遇到更多錯誤,但它並沒有真正做出任何可能,否則很難做到這一點 - 與此參數化相同。 (請糾正我,如果我錯了!)


更新:

  • 我知道這將取決於SQL服務器上(以及客戶端上,但我相信客戶端使用的最佳可能的技術),但大多數情況下我都考慮過MySQL。儘管如此,關於其他數據庫的答案也是受歡迎的。
  • 就我所理解的答案而言,參數化確實不僅僅是簡單地轉義數據。它實際上是以參數化的方式發送到服務器的,所以變量是分開的,而不是作爲單個查詢字符串。
  • 這也使服務器能夠存儲和重複使用不同參數的查詢,從而提供更好的性能。

難道我得到的一切?有一件事我仍然很好奇,是MySQL是否具有這些功能,以及查詢重用是否自動完成(或者如果不是,那麼可以如何完成)。

此外,請有人閱讀此更新時發表評論。我不知道它是否碰到了問題或其他...

謝謝!

回答

6

我確定您的命令和參數的處理方式會因具體的數據庫引擎和客戶端庫而異。

但是,從使用SQL Server的經驗來看,我可以告訴您,使用ADO.NET發送命令時參數會被保留。他們沒有被納入聲明。例如,如果你使用SQL事件探查器,你會看到這樣一個遠程過程調用:

exec sp_executesql N'INSERT INTO Test (Col1) VALUES (@p0)',N'@p0 nvarchar(4000)',@p0=N'p1' 

記住,還有其他的好處,除了防止SQL注入參數保持。例如,查詢引擎可以更好地重用參數化查詢的查詢計劃,因爲語句總是相同的(只是參數值發生更改)。

迴應更新: 查詢參數化非常普遍我期望MySQL(以及任何數據庫引擎)能夠以類似的方式處理它。

基於MySQL協議文檔,它看起來像使用COM_PREPARECOM_EXECUTE數據包處理預處理語句,這些數據包支持二進制格式的單獨參數。目前還不清楚是否所有的參數化語句都會準備好,但看起來好像沒有準備好的語句是由COM_QUERY處理的,它沒有提及參數支持。

如有疑問:測試。如果你真的想知道通過線路發送了什麼,使用像Wireshark這樣的網絡協議分析器並查看數據包。無論內部處理如何以及目前可能提供或不提供給定引擎的任何優化如何,從不使用參數獲得的東西都很少(沒有?)。

+0

+1優秀的論點 - 也指出了巨大的性能優勢(至少在SQL Server環境中)。 – 2012-08-11 21:13:02

+0

感謝您提供更新,希望再次upvote ...如果沒有其他帖子/更新,今晚可能會接受這個答案。 – Luc 2012-08-13 14:36:42

3

參數化查詢被傳遞到SQL的執行參數化查詢,參數從未連接起來以查詢本身,除非執行決定退卻到串聯自己。參數化查詢避免了轉義的需要,並且由於查詢是通用的,並且更有可能數據庫服務器已經緩存了查詢的編譯形式。

2

直接的答案是「它在任何特定實現中都以其實現的方式實現」。有幾十個數據庫,幾十個訪問層,在某些情況下,同一個訪問層處理相同代碼的方式不止一種。

所以,這裏沒有一個正確的答案。

一個例子是,如果您使用Npgsql的查詢不是準備好的語句,那麼它幾乎只是正確地逃避了事情(儘管在Postgresql中的轉義有一些邊緣案例,知道逃避錯過的人,以及Npgsql抓住了他們,所以仍然是一個收益)。通過準備好的語句,它將參數作爲準備狀態參數發送。因此,一種情況允許比另一種更重要的查詢計劃重用。

同一框架(ADO.NET)的SQLServer驅動程序通過調用sp_executesql來傳遞查詢,這允許查詢計劃重用。

除了是,逃逸的事情還是值得考慮的幾個原因:

它每次是相同的代碼。如果你正在逃避自己,那麼無論你每次都是通過同一段代碼來做到這一點(所以它不像使用別人的同一段代碼有任何缺點),或者你冒着每個代碼冒險的風險時間。

他們也不會逃跑。例如,查找'字符的數字的字符串表示中的每個字符都沒有意義。但是不要把它算作不必要的風險,或是合理的微觀優化。那麼,「合理的微觀優化」本身就意味着兩件事之一。要麼不需要精神上的努力來寫或讀後的正確性(在這種情況下,你可能會這麼做),或者它經常發生,所以小節省將會加起來,而且很容易完成。(相關地,編寫高度優化的助手也更有意義 - 所涉及的字符串替換類型是這樣一種情況,其中最常見的替換方法並不像某些其他語言中的某些語言那樣快最少,但只有當方法被稱爲非常多次時,優化纔有意義)。

如果你有一個包含類型檢查參數的庫(無論是基於類型使用的格式,還是通過驗證,這兩種方法都與此類代碼相同),那麼這很容易做到,因爲這些庫以大量使用爲目標,這是一個合理的微觀選擇。

如果您在考慮每次8參數調用的參數編號7是否可能包含'字符,那麼它不是。

如果需要,它們也更容易轉換爲其他系統。再次看看上面給出的兩個例子,除了創建的類之外,儘管SQL-Server和Postgresql具有不同的轉義規則,但您可以使用幾乎與System.Data.SqlClient相同的代碼(與Npgsql一樣)。它們對於二進制字符串,日期時間和其他一些共同的數據類型也有完全不同的格式。

另外,我真的不同意把這稱爲「熱門話題」。它至少在十年內已經有了一個很好的共識。

+0

非常詳細的答案,謝謝! – Luc 2012-08-11 22:24:35