1. 程式人生 > 實用技巧 >SQL 注入原理

SQL 注入原理

SQL 注入原理

sql 注入的原因是應用程式對使用者輸入的資料沒有進行合法判斷,並且直接將資料傳給資料庫進行查詢。

流程如下:

  • 【 P 】:「使用者輸入」資料 P
  • 【 Q = X + P 】:「應用程式」將 P 與其他資料拼接
  • 【 Q --> R 】:「資料庫 」將 Q 作為 SQL語句執行,返回結果 R
  • 【 R 】:「應用程式」接收資料 R,將 R 返回給使用者

基於查詢注入

如果應用程式會直接返回查詢的結果,那麼我們將可以使用基於查詢的注入方法。

聯合查詢法:

  • 【 P 】:「使用者輸入」資料 P = -1' union select @@version --
    [1]
  • 【 Q = X + P 】:「應用程式」拼接資料 Q = SELECT sc FROM score WHERE id='-1' union select @@datadir -- '[2]
  • 【 Q --> R 】:「資料庫 」將 Q 作為 SQL 語句執行,返回結果 R = /var/sql/233.db
  • 【 R 】:「應用程式」接收資料 R,將 R 返回給使用者

基於報錯注入

應用程式不返回查詢結果,反而返回 sql 資料庫的報錯資訊。其流程如下:

  • 【 P 】:「使用者輸入」:P = ' and updatexml(1,concat(1,database()),1)#
  • 【 Q = X + P 】:「應用程式」:Q = SELECT sc FROM score WHERE id='' and updatexml(1,concat(1,database()),1)#'[3]
  • 【 Q --> R 】:「資料庫 」:Q --> R = updatexml 執行錯誤的資訊,其中帶有 concat(1,database()) 的執行結果。
  • 【 R 】:「應用程式」:將 R 返回給使用者

基於布林注入

應用程式不會返回查詢結果,而是會返回一個狀態。比如:

  • 0 , 1
  • 真, 假
  • 高,中,低(當然三種狀態)

其中前兩個只有兩種狀態的資訊被稱為布林值。流程如下:

  • 【 P 】:「使用者輸入」:P = 1' and length((select @@version)) > 0 #[4]
  • 【 Q = X + P 】:「應用程式」:Q = SELECT sc FROM score WHERE id='1' and length((select @@version)) > 0 #'
  • 【 Q --> R 】:「資料庫 」:Q --> R =「真」 或 「假」
  • 【 R 】:「應用程式」:將 R 返回給使用者

基於時間注入

應用程式僅返回一種狀態,意為當前查詢結束。但是沒有返回任何資料或者其他狀態。流程如下:

  • 【 C 】:「計時程式」使用者開始計時 C
  • 【 P 】:「使用者輸入」P = 1' and length((select @@version))>0 and sleep(5) #
  • 【 Q = X + P 】:「應用程式」Q = SELECT sc FROM score WHERE id='1' and length(version()) and sleep(5) > 0 #'
  • 【 Q --> R 】:「資料庫 」Q --> R =「sleep(5),並返回資料」 或 「不執行,返回資料」
  • 【 R 】:「應用程式」將 R 返回給使用者
  • 【 C 】:「計時程式」結束計時 C

可以看出,時間注入與布林注入本質相同。只是前者返回的是 「無延遲,查詢結束」與「延遲 5 秒,查詢結束」這兩種特殊的狀態。

二次注入

之前的注入都是一次輸入,一次輸出式的。而二次注入,則是兩次輸入,兩次輸出。

用一個 web 應用程式舉例,假設它的業務邏輯為:

  • 【 P 】:「使用者輸入」資料 P
  • 【 P --> P' 】:「應用程式」將 P 中的特殊字元進行轉義,得到 P'
  • 【 Q = X + P' 】:「應用程式」將 P' 與其他資料拼接
  • 【 Q --> R 】:「資料庫 」Q 執行後,P' 將轉換[5]為 P 並儲存。同時返回一個結果資訊 R
  • 【 R 】:「應用程式」將 R 返回給使用者
  • 第二次查詢
  • 【 R 】:「使用者輸入」資料 R
  • 【 R --> R' 】:「應用程式」將 R 中特殊字元轉義,得到 R'
  • 【 Q2 = X2 + R' 】:「應用程式」將 R' 與其他資料拼接
  • 【 Q2 --> P 】:「資料庫 」Q2 執行後,返回結果資訊 P[6]
  • 【 Q3 = X3 + P 】:「應用程式」將 P 與其他資料拼接(注意:資料 P 特殊字元沒有被轉義!)
  • 【 Q3 --> R2 】:「資料庫 」Q3 執行後,返回 R2
  • 【 R2 】:「應用程式」將 R2 返回給使用者

從業務看出,第二查詢的過程中,應用程式在拼接資料 P 時沒有對 P 進行轉義。這意味著我們可以在第一次查詢時,將注入語句 P 儲存進資料庫,在第二次查詢執行含有 P 的語句。來成功注入。


  1. 兩個減號之後是有一個空格的。如果這資料通過 url 傳輸的話。需要將空格等價替換成 + 再傳輸。 ↩︎

  2. 可以觀察到資料 P 在 Q 末尾的 WHERE 中,這不是必然,而是常見情況。特殊狀態下,P 可能在 SELECT、GROUP BY 等之中。 ↩︎

  3. updatexml 函式需要三個引數,因此 concat(1,database()) 前後的 1 只是佔位置的引數。 ↩︎

  4. 如果這裡 select @@version 不加括號,將會被 length 認為是變數名。引發語法錯誤。 ↩︎

  5. 資料庫在儲存資料時,會自動去掉資料中的轉義符號。 ↩︎

  6. 這裡的結果 P 就是第一次訪問時儲存的資料 P。 ↩︎