1. 程式人生 > >注入攻擊-SQL注入和程式碼注入

注入攻擊-SQL注入和程式碼注入

注入攻擊

OWASP將注入攻擊和跨站指令碼攻擊(XSS)列入網路應用程式十大常見安全風險。實際上,它們會一起出現,因為 XSS 攻擊依賴於注入攻擊的成功。雖然這是最明顯的組合關係,但是注入攻擊帶來的不僅僅是 XSS。

注入攻擊代指一類攻擊,它們通過注入資料到一個網路應用程式以期獲得執行,亦或是通過非預期的一個方式來執行惡意資料。這種類別的攻擊包括跨站指令碼攻擊(XSS)、SQL 注入攻擊、頭部注入攻擊、日誌注入攻擊和全路徑暴露。當然限於篇幅,這裡只是一個簡單的介紹。

這類攻擊是每個程式設計師的夢魘。它們數量龐大、攻擊範圍廣,並且有時候防禦措施很複雜,因此是最常見、成功率最高的網路攻擊。所有的應用程式都需要從某個地方獲取資料來執行。跨站指令碼攻擊和介面偽裝漏洞最為常見,並且它們本身就已經非常重要,通常與注入攻擊分開歸類。接下來的一章我將單獨介紹它們。

OWASP 對注入攻擊的定義如下:

類似SQL、OS、LDAP注入攻擊等注入攻擊會在不可信資料作為命令或請求的一部分被髮送到解釋程式時發生。攻擊者的惡意資料會迷惑解釋程式去執行非計劃的命令,或訪問非授權的資料。

SQL 注入攻擊

SQL注入

目前最常見的注入攻擊形式是臭名昭著的 SQL 注入攻擊。SQL 注入攻擊不僅常見,而且致命。我要特別強調,瞭解這種攻擊、實現攻擊的條件以及防禦攻擊需要採取的措施極為重要。

SQL 注入攻擊通過將資料注入網路應用程式,然後被用於 SQL 請求來操作。資料通常來自類似網頁表單的不可信來源。不過,資料也可能來自包括資料庫本身在內的其他來源。程式設計師通常會信任來自自己資料庫的資料,以為它們是非常安全的,卻沒有意識到,在一種用法中安全,不代表它在所有其他用法中都是安全的。來自資料庫在經過證明(比如說,通過驗證流程)之前,應該被視為不可信。

如果攻擊成功,SQL 注入攻擊能夠操縱受攻擊的 SQL 請求,從而進行非程式設計師意願的資料庫操作。

看一下這條請求:

git·$db = new mysqli(‘localhost’, ‘username’, ‘password’, ‘storedb’); $result = $db->query( ‘SELECT * FROM transactions WHERE user_id = ‘ . $_POST[‘user_id’] ); ·git

上面的請求中存在多處問題。首先,我們還沒有驗證 POST 資料來確保這是個有效的 userid。其次,我們允許一個不可信來源告訴我們要使用哪個 userid——攻擊者可以任意設定一個有效 userid。也許 userid 包含在一個隱藏的表單欄位,因為網路表單不允許編輯,我們就以為安全了(卻不知道攻擊者可以提交任何資訊)。第三,我們並沒有 escape 該 user_id,或將其作為一個繫結引數傳給請求,由於我們一開始沒有驗證 SQL 請求,這就讓攻擊者有機會注入任意字串來操縱該請求。

上述三點問題在網路應用程式中極其常見。

至於信任來自資料庫的資料,想象一下我們使用 user_name 欄位來搜尋交易。使用者名稱的範圍相當廣闊,可能還包含引用。可以想見,攻擊者可以在一個使用者名稱內儲存一個 SQL 注入字串。如果我們將資料庫視為可信的資料來源,沒能合理地 escape 或約束它,當我們在後續請求中再次使用該字串時,它就可以操縱請求字串。

另一個需要注意的 SQL 注入攻擊因素是永久儲存不需要總是在伺服器上進行。HTML5 支援使用客戶端資料庫,可以藉助 Javascript 使用 SQL 來查詢。有兩個支援這項操作的介面:WebSQL 和 IndexedDB。WebSQL 於2010年被 W3C 棄用,受到後臺使用 SQLite 的 WebKit 瀏覽器支援。雖然這個介面不被推薦使用,但是 WebKit 處於後臺相容考慮,很有可能會繼續支援它。正如它的名字所示,它接收 SQL 請求,因此容易遭受 SQL 注入攻擊。IndexedDB 是一個新的備選,不過它是一種 NOSQL 資料庫(不需要使用 SQL 查詢)。

SQL 注入攻擊範例

嘗試操縱 SQL 命令的目標包含以下幾種:

  1. 資訊洩露
  2. 披露儲存資料
  3. 操縱儲存資料
  4. 避開許可權管理
  5. 客戶端 SQL 注入攻擊

資訊洩露

披露儲存資料

操縱儲存資料

避開許可權管理

防禦 SQL 注入攻擊

防禦 SQL 注入攻擊可採用深度防禦原則。在將資料用於 SQL 命令之前,應該進行驗證,以確保它是我們期望的正確格式,並且在將資料包含在請求或繫結引數前,應該將其 escape。

驗證

第二章講述輸入驗證,而且正如我在其中提到的,我們應該假設不是由當前請求的 PHP 原始碼直接生成的所有資料都不可信。對其嚴格驗證,並且拒絕所有未通過驗證的資料。不要嘗試“修復”資料,除非只是簡單修正資料格式。

常見的驗證錯誤包括只驗證資料當下用途(例如,展示或計算),卻不考慮資料最終儲存位置的資料庫表字段的驗證需求。

Escaping

通過使用mysqli 擴充套件,你可以利用 mysqlirealescapestring() 函式來 escape包含在 SQL 查詢中的所有資料。PostgresSQL 的 pgsql 擴充套件提供 pgescapebytea()、 pgescapeidentifier()、 pgescapeliteral() 和 pgescape_string() 函式。Mssql(微軟 SQL 伺服器)不提供 escaping 功能,而經常被推薦的 addslashes() 方法並不夠用——你實際上需要一個定製功能http://stackoverflow.com/questions/574805/how-to-escape-strings-in-mssql-using-php。

再告訴你一件頭疼的事,你絕對絕對不能在 escape 進入 SQL 查詢資料上出錯。一旦失手,可能就會引發 SQL 注入攻擊。

基於以上原因,並不推薦使用 escaping。它可以用來救急,如果你用來抽象的資料庫程式庫不強制引數繫結就能進行 SQL 查詢,可能就需要使用它。否則你應該避免使用 escape。它很混亂,容易出錯,而且因資料庫擴充套件不同而存在差異。

引數化查詢(預處理語句)

引數化或引數繫結是構建 SQL 查詢的推薦方法,而且所有優秀的資料庫程式庫都預設使用這種方法。以下是使用 PHP 的 PDO 擴充套件的一個例項。

if(ctype_digit($_POST['id']) && is_int($_POST['id']))  
{     
      $validatedId = $_POST['id'];     
      $pdo = new PDO('mysql:store.db');     
      $stmt = $pdo->prepare('SELECT * FROM transactions WHERE user_id = :id');     
      $stmt->bindParam(':id', $validatedId, PDO::PARAM_INT);     
      $stmt->execute(); 
} else {     
// reject id value and report error to user 
} 

PDO 語句可用的bindParam() 方法讓你可以給預處理語句中出現的佔位符繫結引數,並且接受基本的資料型別引數,例如 PDO::PARAMINT、 PDO::PARAMBOOL、 PDO::PARAMLOB 和 PDO::PARAMSTR。這種方法預設使用 PDO::PARAM_STR,因此記得對其他數值做相應調整!

不同於手動 escape,這種方式下的引數繫結(或者你的資料庫使用的方法)會自動正確地 escape 繫結的資料,因此你不需要回憶之前用了哪種 escape 函式。持續使用引數繫結要比記著手動 escape 所有東西要可靠得多。

強制實施最小特權原則

制止已經發生的 SQL 注入攻擊跟從一開始就防禦同樣重要。一旦攻擊者獲得執行 SQL 查詢的能力,他們就會以一個數據庫使用者的身份進行查詢。可以通過確保所有資料庫使用者只得到完成各自任務必需的許可權,來執行最小特權原則。

如果一個數據庫使用者擁有很大的許可權,攻擊者就可能刪除資料表,操縱其他使用者的許可權,從而發起其他 SQL 注入攻擊。你絕對不能以超級使用者、其他許可權較高或管理員層級的使用者身份訪問網路應用程式的資料庫,從而杜絕這種情況發生。

最小特權原則的另外一個變體是區別資料庫的讀資料和寫資料許可權。你可以設定一個擁有寫資料許可權的使用者,和另一個只有讀資料許可權的使用者。這種角色區分可以確保在 SQL 注入攻擊目標為只讀使用者時,攻擊者無法寫資料或操縱表資料。這種生物隔離區劃可以延伸到進一步限制訪問許可權,這樣就可以將 SQL 注入攻擊的影響最小化。

很多網路應用程式,尤其是開源應用程式,特別被設計成只有一個數據庫使用者,而且幾乎從來不會有人檢查該使用者是不是高度特免的。記住以上觀點,忍住誘惑,不要在一個管理員使用者下執行這樣的網路應用程式。

程式碼注入攻擊(也叫Remote File Inclusion)

程式碼注入攻擊指的是任何允許攻擊者在網路應用程式中注入原始碼,從而得到解讀和執行的方法。這並不適用於對應用程式客戶端的程式碼注入攻擊,例如 Javascript,那屬於跨站指令碼攻擊(XSS)。

原始碼可以通過不可信的輸入直接注入,或者網路應用程式在通過本地檔案系統或類似 URL 這樣的外部來源載入程式碼時被操縱。包含遠端檔案導致程式碼注入攻擊的情況通常被稱為遠端檔案包含漏洞,雖然遠端檔案包含攻擊本身的目的就是為了注入程式碼。

造成程式碼注入攻擊的初始原因包括輸入驗證失敗,包含可能被當做 PHP 程式碼、任何語境下的不可信輸入,未能保障原始碼庫的安全,在下載第三方程式庫時不夠謹慎,伺服器配置不當導致非 PHP 檔案通過網路伺服器被傳送到 PHP 解釋程式。最後一點尤其要加以注意,因為它意味著不可信使用者上傳到伺服器的所有檔案都可能帶來極大的風險。

程式碼注入攻擊範例

眾所周知,PHP 涉及無數程式碼注入攻擊目標,因此任何一位程式設計師都要高度關注程式碼注入攻擊問題。

檔案包含攻擊

程式碼注入攻擊最明顯的目標就是 include()、 includeonce()、 require() 和 requireonce() 函式。如果不可信輸入被允許來決定傳輸給這些函式的路徑引數,它就可能影響被包含的本地檔案。應該注意的是,被包含的檔案不一定是一個真正的 PHP 檔案,任何能夠攜帶文字資料(例如,幾乎所有的)的被包含檔案都有可能。

路徑引數也可能遭受目錄遍歷攻擊或遠端檔案包含攻擊。在路徑中使用 ../ 或 ..(dot-dot-slash) 字串會讓攻擊者能夠觸及 PHP 流程能夠訪問的所有檔案。除非 XXX 被禁用,否則以上函式還能接受 PHP 預設設定中的 URL。

評估

PHP 的 eval() 函式接收 PHP 程式碼字串並執行。

正則表示式注入攻擊

PHP 中 PCRE 的 pregreplace() 函式允許“e”(PREGREPLACE_EVAL)修飾符,這就意味著替換字串在替換後將被當成 PHP。用於替換字串的不可信輸入可能會注入即將執行的 PHP 程式碼。

缺陷檔案包含邏輯攻擊

按照定義,網路應用程式會包含滿足各種需求所需的各類檔案。通過操縱請求路徑或請求引數,它可以利用伺服器路由的缺陷邏輯、依賴管理、自動載入或其他流程,導致伺服器包含非預期的本地檔案。

這些超出網路應用程式設計初衷的操縱可能帶來無法預料的影響。比如說,一個應用程式可能無意中暴露了只用於命令列用法的路徑。該應用程式可能還暴露了建構函式用來執行任務的其他類(雖然並不推薦這種設計類的方法,不過還是有人這麼做)。兩種場景都有可能干擾應用程式的後臺執行,導致本來不應該被直接訪問的資源密集型執行活動遭受資料操縱或拒絕服務攻擊(DOS)。

伺服器配置不當

程式碼注入攻擊的目標

由於程式碼注入攻擊允許攻擊者選擇任意 PHP 程式碼來執行程式碼注入攻擊的目標極其廣泛。

防禦程式碼注入攻擊

命令注入攻擊

命令注入攻擊範例

防禦命令注入攻擊

原文地址:Injection Attacks