天天看點

《Java安全編碼标準》一2.1 IDS00-J淨化穿越受信邊界的非受信資料

許多程式會接受來自未經驗證的使用者資料、網絡連接配接,以及其他來自于非受信域的不可信資料,然後它們會将這些資料(經過修改或不經修改)穿過受信邊界送到另一個受信域中。通常,這些資料是以字元串的形式出現的,并且它們會有既定的内部資料結構,這種資料結構必須能夠被子系統所解析。同時,必須淨化這些資料,因為子系統可能并不具備處理惡意輸入資訊的能力,這些未經淨化資料輸入很有可能包含注入攻擊。

值得注意的是,程式必須對傳遞給指令行解釋器或者解析器的所有字元串資料進行淨化,這樣才能保證解析器處理後或者解釋器處理後的字元串是無害的。

許多指令行解釋器和解析器提供了自己的資料淨化和資料驗證的方法。當存在這樣的方法時,應當優先考慮它們,因為相對而言,自定義的淨化方法會忽略一些特殊的情況,或者會忽略在解析器中所隐含着的那些複雜性。另一個問題是,當有新的功能添加到指令行解釋器或者添加到解析器軟體的時候,自定義的處理方法可能并不能得到很好的維護。

當初始的sql查詢被修改成另一個完全不同形式的查詢的時候,就會出現sql注入漏洞。執行這一被修改過的查詢,可能會導緻資訊洩露或者資料被修改。防止sql注入漏洞的主要方法是,淨化并驗證非受信輸入,同時采用參數化查詢的方法。

假設一個資料庫具有使用者名和密碼資料,它可以用這些資料來對系統使用者進行認證。每個使用者名使用長度為8的字元串表示,密碼使用長度為20的字元串表示。

一個用來驗證使用者的sql指令如下所示:

如果它可以傳回任何記錄,那麼意味着使用者名和密碼是合法的。

然而,如果攻擊者能夠替代和中的任意字元串,它們可以使用下面的關于< username >的字元串進行sql注入:

當将其注入到指令時,指令就會變成:

如果validuser是一個有效的使用者名,那麼這條選擇語句會選擇出表中的validuser?記錄。這個操作中不會使用密碼,因為username='validuser'的判斷條件為真;是以,不會檢查那些在or後面的條件。是以,隻要在or後面的部分是一個文法正确的sql表達式,攻擊者就可以由此獲得validuser的通路權限。

同樣,攻擊者可以為提供字元串:

這将會産生以下的指令:

這一次,‘1’=‘1’是永遠成立的,它使得使用者名和密碼的驗證是無效的,并且攻擊者可以不需要正确的使用者名或密碼就能登入。

在這個不符合規則的代碼示例中,系統使用jdbc代碼來認證使用者。密碼通過char數組傳入,建立資料庫連接配接,然後進行哈希編碼。

遺憾的是,這段代碼會出現sql注入問題,因為在sql語句中,sqlstring?允許輸入未經淨化的輸入參數。如前所述的攻擊場景将再次出現。

幸運的是,在jdbc類庫中,提供了能夠建構sql指令并且處理非受信資料的api。在java.sql.preparedstatement類中,可以對輸入字元串進行轉義,如果使用正确的話,可以防止sql注入。下面的例子顯示了基于元件的淨化過程:

這個符合規則的方案修改了doprivilegedaction()方法,使用preparedstatement類來代替?java.sql.statement。并且這段代碼會驗證username參數的長度,防止如果送出一個長使用者名的時候可能會出現的攻擊。

通過使用preparedstatement?類的set*()方法,可以進行強類型檢查。這樣可以減少sql注入漏洞,因為自動化例程會正确地轉義雙引号内的輸入資料。需要注意的是,那些将資料插入資料庫的查詢也會使用preparedstatement?。

xml注入

由于xml語言具有平台無關性、靈活性和相對簡潔的特點,它适用于從遠端過程調用到系統化存儲、交換以及擷取資料的各種場合。然而,因為xml的多功能性,是以xml也是被廣泛攻擊的對象。其中一種攻擊稱為xml注入。

如果使用者有能力使用結構化xml文檔作為輸入,那麼他能夠通過在資料字段中插入xml标簽來重寫這個xml文檔的内容。當xml解析器對這些标簽進行解析和歸類的時候,會将它們作為可執行的内容,進而導緻重寫許多資料成員。

下面是一段從一個線上商店應用中摘取出來的xml代碼,主要用來查詢背景資料庫。使用者可以指定該次購買的物品的數量。

惡意使用者可以在quantity?域中輸入以下字元串來代替一個簡單的數字:

結果會導緻生成如下xml文檔:

通過用于xml的簡單api(sax)解析器(org.xml.sax?和javax.xml.parsers.saxparser)可以解釋該xml檔案,這時第二個price域會覆寫第一個price域,進而使商品的價格被設定為$1。甚至于存在這樣的可能,攻擊者可以構造這樣一個攻擊:插入特殊字元,比如插入注釋塊或者cdata分隔符,進而就可以扭曲xml文檔正常表達的意思。

在下面這段不符合規則的代碼示例中,一個客戶方法使用簡單的字元串連結來建立一個xml查詢,然後将其發送到伺服器。在這時就有可能出現xml注入問題,因為這個方法并沒有進行任何輸入驗證。

根據特定的資料和接收這些資料的指令解釋器或者解析器的情況,必須使用一個适當的方法來淨化這些非受信的使用者輸入。這個符合規則的方案使用白名單來淨化輸入資料。在這個方案中,處理方法要求輸入的數字必須為0~9。

一個更為通用的檢查xml以防止注入的方法是,可以使用文檔類型定義(document type definition,dtd)或模闆(schema)來進行驗證。模闆必須嚴格定義,進而防止通過注入的方式将一個合法的xml文檔變成錯誤的文檔。以下是一個用來驗證xml文檔片段的合适的模闆:

在schema.xsd檔案中可以得到xml模闆。在這個符合規則的方案中,可以采用這個模闆來防止xml注入。它同時需要使用customresolver?類來防止xxe攻擊。關于這個類和所謂的xxe攻擊,可以參見如下代碼示例中的描述。

當xml可能已經載入還未處理的輸入資料時,一般情況下使用xml模闆或者dtd驗證xml。如果還沒有建立這樣的xml字元串,那麼在建立xml之前處理輸入,這種方式性能

較高。

xml外部實體攻擊(xxe)

一個xml文檔可以從一個被稱為實體的很小的邏輯塊開始動态建構。實體可以是内部的、外部的或基于參數的。外部實體允許将外部檔案中的xml資料包含進來。

根據xml w3c 4.4.3小節的建議?[w3c 2008]:“包含所需的驗證”部分。

當一個xml處理器找到一個已經解析過的實體的引用的時候,為了驗證這個文檔,處理器必須包含它的替代字段。如果該實體是外部的,而且處理器不打算驗證這個xml文檔,那麼處理器可以(但不必)包含該實體的替代字段。

攻擊者通過操作實體的uri,使其指向特定的在目前檔案系統中儲存的檔案,進而造成拒絕服務攻擊或者程式崩潰,比如,指定/dev/random或者/dev/tty作為輸入uri。這可能永久阻塞程式或者造成程式崩潰。這就稱為xml外部實體攻擊(xml external entity, xxe)。因為包含來作為外部實體的替代文本并不是必需的,并不是所有的xml解析器都存在這樣的外部實體攻擊的安全漏洞。

下面這個不符合規則的代碼示例嘗試對evil.xml檔案進行解析,并報告相關錯誤,然後退出。然而,sax或者dom(document object model, 文檔對象模型)解析器會嘗試通路在system屬性中辨別的url,這意味着它将讀取本地/dev/tty檔案的内容。在posix系統中,讀取這個檔案會導緻程式阻塞,直到可以通過計算機控制台得到輸入資料為止。結果是,攻擊者可以使用這樣的惡意xml檔案來導緻系統挂起。

如果evil.xml檔案中包含以下文本,程式會受到遠端xxe攻擊。

如果包含在異常裡的資訊是敏感的,則這個不符合規則的代碼示例同樣違反了規則err06-j。

這個符合規則的方案定義了一個customresolver類?,這個類實作了org.xml.sax.entityresolver接口。它可以讓sax應用定制對外部實體的處理。setentityresolver()方法可以将對應的sax驅動執行個體注冊進來。這個定制的處理器使用的是一個為外部實體定義的簡單的白名單。當輸入不能解析任何指定的、安全的實體源路徑的時候,resolveentity()方法會傳回一個空的inputsource?對象。結果是,當解析惡意輸入時,這個由自定義的解析器傳回的空的inputsource對象會抛出java.net.malformedurlexception異常。需要注意的是,你必須建立一個xmlreader對象,以便通過這個對象來設定自定義的實體解析器。

下面是一個基于元件的淨化示例。

如果不在系統處理或存儲使用者輸入之前淨化資料,會導緻注入攻擊。

《Java安全編碼标準》一2.1 IDS00-J淨化穿越受信邊界的非受信資料

相關的漏洞 cve-2008-2370描述了在aapache tomat 從4.1.0到4.1.37, 5.5.0 到 5.5.26以及 6.0.0 到6.0.16這些版本中的安全漏洞。當使用requestdispatcher時,tomcat會進行路徑标準化,然後從uri中移除查詢字串,這樣會導緻攻擊者可以進行遠端目錄周遊攻擊,并且在請求參數中通過..(兩個點)來讀取任意檔案。

《Java安全編碼标準》一2.1 IDS00-J淨化穿越受信邊界的非受信資料
《Java安全編碼标準》一2.1 IDS00-J淨化穿越受信邊界的非受信資料