安全相關,SQL 注入

什麼是 SQL 注入

魯迅

在全部漏洞類型中,SQL 注入但是說是危害最大最受你們關注的漏洞。簡單說來,SQL 注入是經過在用戶可控參數中注入SQL語法,破壞原有SQL結構,達到編寫程序時意料以外結果的攻擊行爲。php

ThinkJS 爲例,假設咱們寫了以下一個接口(實際狀況確定不會這麼寫的):html

// user.js
module.exports = class extends think.Controller {
  async loginAction() {
    const { username, password } = this.post();
    const user = await this.model().query(
      `SELECT * FROM user WHERE name = "${username}" AND password= "${password}"`
    );

    if (think.isEmpty(user)) {
      return this.fail();
    }
    return this.success(user);
  }
}

當用戶提交的 username 是 admin"; -- 的話,最終執行的 SQL 語句就會變成mysql

SELECT * FROM user WHERE name = "admin"; --" AND password= "111"

最終攻擊者就能夠成功登陸 admin 帳號了,這就是最簡單的 SQL 注入了。從上面這個簡單示例中,咱們發現漏洞成因能夠歸結爲如下兩個緣由疊加形成的:git

  1. 程序編寫者在處理應用程序和數據庫交互時,使用字符串拼接的方式構造SQL語句。
  2. 未對用戶可控參數進行足夠的過濾便將參數內容拼接進入到SQL語句中。

SQL注入根據攻擊者獲取數據的方式分爲回顯注入報錯注入以及盲注github

剛纔演示的直接從返回結果中獲取數據則爲回顯注入,固然也能夠經過 MySQL 執行的報錯結果中嗅探到數據庫的結構和內容,這就是報錯注入。盲注則是根據數據庫執行的延時等操做來判斷是否接近正確值,簡單的說來有點像是拿着聽診器試探保險箱的密碼的感受。web

不一樣的分類原則會有不一樣的分類,也有按照注入位置及方式不一樣進行分類分爲POST注入GET注入cookie注入盲注延時注入搜索注入base64注入等。sql

不過你們都支持分類形式不一樣,原理仍是一致的,這裏就不一一細說了。shell

SQL 注入的危害

若是網站存在 SQL 注入漏洞,至關於將數據庫直接暴露在攻擊者面前,可想而知危害會有多大了。攻擊者利用 SQL 注入漏洞能實現如下攻擊:數據庫

  1. 跳過帳戶權限驗證達到越權
  2. 獲取數據庫關鍵信息從而進行脫庫
  3. 在特別狀況下還能夠修改數據庫內容或者插入內容到數據庫,若是數據庫權限分配存在問題,或者數據庫自己存在缺陷,那麼攻擊者能夠經過SQL注入漏洞直接獲取webshell或者服務器系統權限。

防護方法

數據校驗

從文章開頭能夠看到,其實漏洞的主要緣由仍是沒有對用戶輸入的數據進行過濾,因此對來自用戶的數據(GET, POST, cookie 等)最好作到如下兩種過濾校驗數組

  1. 檢查輸入的數據是否具備所指望的數據格式。這種在參數是數字的時候特別有效,若是攻擊者選擇在參數中插入內容的話則會被轉換成 NaN 致使攻擊失敗。在 ThinkJS 中咱們提供了強大的 Logic 功能能夠方便的對數據進行格式校驗。
  2. 使用數據庫特定的敏感字符轉義函數把用戶提交上來的非數字數據進行轉義。在 ThinkJS 中封裝了 escapeString() 方法能夠對敏感字符進行轉義,其原理則和 PHP 的 mysql_escape_string() 方法是一致的。

檢查輸入數據格式在 ThinkJS 中還能防止另一種非通用 SQL 安全問題。文章開頭的示例代碼咱們在實際的應用中通常會這麼寫:

// user.js
module.exports = class extends think.Controller {
  async loginAction() {
    const { username, password } = this.post();
    const user = await this.model('user').where({
      name: username,
      password
    }).find();

    if (think.isEmpty(user)) {
      return this.fail();
    }
    return this.success(user);
  }
}

當咱們構造如 name=admin&password[]=!%3D&password[]= 的請求參數時,最終執行的 Model 語句就會變成

this.model('user').where({name: 'admin', password: ['!=', '']});
因爲 HTTP 請求的自動合併數組的特性形成了咱們的 SQL 語句並不是是咱們想要的效果。雖說框架自己已經針對這種狀況進行了 處理,當用戶輸入參數被認爲是 SQL 運算符時則會將關鍵字增長空格,從而將其變成普通字符串避免這個問題。
不過這種方法會有 必定的損傷,畢竟當真的要傳這幾個運算符的狀況的時候接收到的數據和請求的不同仍是有點 懵逼的。因此最好仍是在 Logic 層對數據進行完善的校驗將問題前置比較好。

權限限制

嚴格限制Web應用的數據庫的操做權限,給此用戶提供僅僅可以知足其工做的最低權限,從而最大限度的減小注入攻擊對數據庫的危害。

**請記住永遠不要使用超級用戶或全部者賬號去鏈接數據庫!**

當數據庫被攻擊時將損傷限制在當前表的範圍是比較明智的選擇。經過權限限制能夠防止攻擊者獲取數據庫其它信息,甚至利用數據庫執行 Shell 命令等操做。

日誌處理

當數據庫操做失敗的時候,儘可能不要將原始錯誤日誌返回,好比類型錯誤、字段不匹配等,把代碼裏的 SQL 語句暴露出來,以防止攻擊者利用這些錯誤信息進行 SQL 注入。

除此以外,在容許的狀況下,使用代碼或數據庫系統保存查詢日誌也是一個好辦法。

顯然,日誌並不能防止任何攻擊,但按期審計數據庫執行日誌能夠跟蹤是否存在應用程序正常邏輯以外的 SQL 語句執行。日誌自己沒用,要查閱其中包含的信息才行。畢竟,更多的信息總比沒有要好。

後記

其實在 ThinkJS 中已經將關鍵字處理類的方法已經集成,使用程序提供的 ORM 方法進行 SQL 構造會比本身寫 SQL 語句拼接來的更方便,同時也能提升項目代碼複用,減小潛在的風險。

若是對 ThinkJS 默認的 think-model 不喜歡的話,也可使用其它第三方的 ORM 框架,例如 think-sequelize

相關文章
相關標籤/搜索