董沛然
(國家開發(fā)銀行信息科技部,北京 100032)
軟件的注入類漏洞,是風險等級很高的一類漏洞,在CVE、OWASP等權威信息安全漏洞庫中,注入類漏洞長期位居前10名之列。它可以導致系統(tǒng)運行錯誤、或泄露客戶信息等嚴重事故。
在所有注入類漏洞中,尤以SQL注入最為常見,危害也最大。
SQL注入,簡單地說就是利用程序代碼漏洞,繞過程序權限,將SQL命令插入到用戶請求的查詢字符串或者輸入域進行攻擊,其結果輕則獲得敏感信息和數(shù)據(jù),重則控制服務器[1]。
攻擊者(黑客)在利用SQL漏洞實施攻擊時,通常會選擇直接注入或二次注入兩種方式。在直接SQL注入方式中,直接將代碼插入到用戶輸入變量,該變量與SQL命令串聯(lián)在一起;在二次SQL注入方式中,將惡意字符串通過報文植入數(shù)據(jù)庫表中,未來在程序讀取該庫表字段時,被動態(tài)拼接為SQL命令,并執(zhí)行。
下面以一個簡單的直接注入SQL漏洞為例,說明SQL注入的原理。以下代碼動態(tài)地構造并執(zhí)行了一個SQL查詢,該查詢可以搜索與指定客戶名稱相匹配的交易記錄。
假設有如下C語言代碼段:
Receive_Input(&szCUSTOM_NAME); //接收用戶輸入的查詢條件——注入點
上述代碼的邏輯是:接收用戶在界面輸入的姓名,將其作為查詢條件拼接到SQL語句中,再在數(shù)據(jù)庫中完成查詢操作。
正常情況下,當用戶輸入客戶名稱David時,拼接成的SQL語句為:
select * from TABLE_TRANSACTION where CUSTOM_NAME='David';
此時程序將產生正確的輸出。
但是當這段代碼被黑客攻擊時,如果黑客輸入的內容為:
這樣一來,將會有兩條SQL語句被執(zhí)行,雖然其中前一條SQL語句是安全的,但后一條卻可以查詢出庫表中的所有記錄,造成嚴重的信息泄露。
這里涉及一些概念。在這個例子中,程序從用戶輸入獲取了szCUSTOM_NAME字段,在未經過濾的情況下,將其值直接以字符串拼接的方式拼接到一個SQL語句里面,造成了SQL注入漏洞。
在這段程序中,直接從用戶輸入獲取信息的代碼行Receive_Input(&szCUSTOM_NAME)稱為這個SQL注入漏洞的注入點。執(zhí)行危險SQL語句的代碼行Execute(szSQL)稱為這個SQL注入漏洞的爆發(fā)點。從注入點到爆發(fā)點之間,所有涉及危險字符串szCUSTOM_NAME信息賦值的語句,稱為這個SQL注入漏洞的傳遞鏈。
命令注入漏洞,是指通過提交惡意構造的參數(shù)破壞命令語句的結構,達到非法執(zhí)行命令的手段。命令注入漏洞常發(fā)生在具有執(zhí)行系統(tǒng)命令的Web應用中。
下面舉一個命令注入漏洞的例子。以下代碼在服務器上動態(tài)地為用戶建立專用目錄,路徑名是用戶輸入的客戶名稱。
假設有如下PHP語言代碼段:
如此看來上述代碼也是存在很大風險的。
在本例中,同樣涉及注入點、爆發(fā)點、數(shù)據(jù)流的概念。從用戶輸入獲取cu st om Na me的代碼行$customName = $_POST["customName"]稱為這個命令注入漏洞的注入點。執(zhí)行這個危險命令的代碼行system($command)稱為這個命令注入漏洞的爆發(fā)點。從注入點到爆發(fā)點之間,所有涉及危險字符串customName信息賦值的語句,稱為這個命令注入漏洞的傳遞鏈。
從上面兩個例子可以看出,注入類漏洞的本質是,黑客通過引號、分號、斜杠、點號等特殊字符,閉合了原有的程序邏輯,使得用戶提交的參數(shù)和程序本來的邏輯相互干擾。這樣一來,黑客提交的參數(shù)就不僅僅只起到參數(shù)的作用,而且還進入到程序邏輯里來了。黑客就是通過這樣的方式,把惡意代碼嵌入到正常代碼里來的。
根據(jù)注入類漏洞的可利用性和危害性,目前業(yè)界一般將其分為三類。
(1)高危漏洞:可以直接被利用的漏洞,并且利用難度較低。利用之后可能對網站或服務器的正常運行造成嚴重影響、對用戶財產及個人信息造成重大損失。
(2)中危漏洞:利用難度極高,或滿足嚴格條件才能實現(xiàn)攻擊的漏洞,或漏洞本身無法被直接攻擊,但能為進一步攻擊起較大幫助作用的漏洞。
(3)低危漏洞:無法直接實現(xiàn)攻擊,但可能造成一些非重要信息的泄露,這些信息可能讓攻擊者更容易找到其他安全漏洞。
從上節(jié)可見,注入漏洞的本質是利用程序代碼缺陷,繞過程序的權限,將惡意代碼插入到用戶請求的查詢字符串或者輸入域進行攻擊。
為了防范注入類漏洞,業(yè)界主流的做法是加強對用戶輸入內容的校驗。通常在各種大型信息系統(tǒng)的界面中,諸如時間、日期等大多數(shù)輸入框的格式是固定的,黑客無法通過這些輸入框注入攻擊信息。但是對于姓名、備注欄等較為開放的輸入框,系統(tǒng)一般不對輸入內容做過多校驗,并且此類輸入框的長度一般較長,足夠黑客輸入可運行的惡意內容。
有鑒于此,出于防范注入類型攻擊的目的,筆者認為進行大型信息系統(tǒng)的開發(fā)時應做到以下5點。
在編碼層面,編寫安全性高的源代碼,避免SQL拼接,改用占位符方式實現(xiàn)SQL組裝。重視源代碼安全檢查,因為源代碼安全檢查可以暴露一部分明顯的安全隱患(如SQL拼接等)[2]。
在前面的案例中,造成SQL注入攻擊的根本原因在于攻擊者可以改變SQL查詢的上下文,使本應作為數(shù)據(jù)解析的數(shù)值,被篡改為命令了。為避免這種問題,以占位符形式給SQL語句傳參是一種十分有效的方法。
使用占位符生成SQL語句的示例代碼段如下:
在上面的示例代碼段中,問號表示占位符。在程序編譯時將參數(shù)傳入SQL語句,生成合法的SQL語句。如此一來,非程序自身的數(shù)據(jù)不參與SQL語句邏輯的構成,那么黑客精心設計的危險字符串也就不會奏效了。
對所有開放型輸入框,進行惡意關鍵字的校驗,如前面案例中的delete、rm、分號、斜杠等字符串應盡量過濾掉。危險字符串可用窮舉或正則表達式的方式識別[3]。過濾時,可以考慮去掉這些可能隱藏惡意攻擊意圖的字符串,或用星號替換。
堅持最小展現(xiàn)原則,客戶端的展示信息不宜過細。這是因為黑客在利用注入手段攻擊系統(tǒng)時,往往免不了“猜”和“試”的過程。黑客通常通過反復試探,并借助系統(tǒng)的各種返回信息、消息、日志內容來猜測其開發(fā)語言、后臺架構、甚至庫表字段名稱等細節(jié)信息[4]。因此,我們在軟件開發(fā)中,要盡量避免給用戶暴露系統(tǒng)細節(jié)(如應用程序信息、數(shù)據(jù)庫信息、或其他容易暴露后臺邏輯的信息)[5]。不但要避免在客戶端上顯式地展示這些信息,也不宜寫在消息或日志里返回給客戶端,因為黑客可以利用流量抓包軟件抓取到這些消息或日志信息。
系統(tǒng)的每一個進程、每一次數(shù)據(jù)庫操作,應該使用可以完成該任務的最小權限運行。任何需要提權的操作,都應盡可能只保持最短的時間,一旦任務完成,應該立即收回權限,這樣可以減少攻擊者在高權限的條件下執(zhí)行惡意代碼的機會。
系統(tǒng)在進行惡意關鍵字的過濾時,不僅需要考慮本系統(tǒng)所用的開發(fā)語言,還應考慮有關聯(lián)關系的其他下游系統(tǒng)。一個大型復雜系統(tǒng),往往是由眾多小型子系統(tǒng)構成的。如圖1所示。
圖1 大型復雜系統(tǒng)模型(無輸入校驗)
其中,接入渠道子系統(tǒng)A主要負責接收用戶的輸入信息,并做一些簡單的處理。而后,根據(jù)一定的邏輯,A將數(shù)據(jù)流輸入到后臺處理子系統(tǒng)B和后臺處理子系統(tǒng)C,進行一些更為復雜的處理。
如前所述,由于系統(tǒng)遭受的注入類攻擊主要來自于用戶輸入,所以接入渠道系統(tǒng)一般擁有比較完善的校驗規(guī)則,如下圖所示,接入渠道子系統(tǒng)A的輸入校驗模塊應該可以過濾針對本子系統(tǒng)A的惡意字符。而后臺處理子系統(tǒng)B和C由于并不直接面向用戶,所以在開發(fā)時,往往這類子系統(tǒng)的安全性校驗就不那么完整和嚴謹了。如圖2所示。
圖2 大型復雜系統(tǒng)模型(在接入渠道子系統(tǒng)A中加入輸入校驗)
但是,經過接入渠道子系統(tǒng)A處理過的數(shù)據(jù)會流入后臺處理子系統(tǒng)B和C中,并且B、C所用的開發(fā)語言、數(shù)據(jù)庫、部署的操作系統(tǒng)可能與接入渠道系統(tǒng)完全不同,黑客的惡意內容完全有可能躲過了前面的校驗,而待轉入后面的系統(tǒng)后發(fā)起攻擊。這就如同地鐵線路中,一顆炸彈一旦從某站流入地鐵,就可以暢通無阻地流竄到任意線路、任意站點。所以要想保證所有線路的安全,就要確保全市任一地鐵站,與重要站點的安檢同樣嚴格。因此更好的做法是,在每個子系統(tǒng)中,都針對本子系統(tǒng)的技術特點,進行有針對性的校驗。如圖3所示。
圖3 大型復雜系統(tǒng)模型(在每個子系統(tǒng)中都加入輸入校驗)
當然,這種各個子系統(tǒng)分別校驗的方式容易形成“各自為政”,可能接入渠道子系統(tǒng)A中已經實現(xiàn)了的輸入校驗,在后臺處理子系統(tǒng)B和C中又重復實現(xiàn)了,這必然會降低系統(tǒng)的開發(fā)效率和運行效率。
有鑒于此,最好的方式是在接入渠道子系統(tǒng)A的前面,建立專門的校驗系統(tǒng),涵蓋針對所有子系統(tǒng)的潛在危險字符校驗,供整個系統(tǒng)群使用。如圖4所示。
圖4 大型復雜系統(tǒng)模型(在接入渠道子系統(tǒng)A前加入專門的輸入校驗模塊)
這樣不僅達到了全面校驗的效果,并且也避免了重復校驗的危害,充分保證系統(tǒng)群的整體運行效率。不僅如此,從開發(fā)人員安排上,可以請專門的信息安全人員編寫此安全模塊(或子系統(tǒng)),在保證專業(yè)性的同時,也有利于其他子系統(tǒng)的開發(fā)人員將精力集中于業(yè)務處理上。
本文首先介紹了注入類安全缺陷的定義和原理,然后,從安全架構、安全編碼的角度,提出了5點防范注入類缺陷的措施:以占位符形式給SQL語句傳參、嚴格進行惡意關鍵字的校驗、客戶端的展示信息不宜過細、堅持最小權限原則、遵循系統(tǒng)的整體安全架構模型。最后,從安全測試的角度,提出了基于復合引擎的自動化代碼安全檢查平臺的構建方法,并闡述了其優(yōu)勢、原理和具體算法。■