天天看點

《Java安全編碼标準》一2.11 IDS10-J不要拆分兩種資料結構中的字元串

在曆史遺留系統中,常常假設字元串中的每一個字元使用8位(一個位元組,java中的byte)。而java語言使用16位表示一個字元(java中的char類型)。遺憾的是,不管是java的byte類型還是char類型資料,都不能表示所有的unicode字元。許多字元串使用例如utf-8編碼的方式存儲和通信,而在這種編碼中,字元長度是可變的。

當java字元串以字元數組的方式存儲時,它可以用一個位元組數組來表示,字元串裡的一個字元可以用兩個連續的或更多的byte類型或者char類型表示。如果拆分一個char類型或byte類型的數組,将會對多位元組的字元産生風險。

如果忽略那些補充字元(supplementary character)多位元組字元或者整合字元(修改其他字元的那些字元),攻擊者可能繞過輸入驗證。是以,不應該拆分兩種資料結構中的字元。

在一些字元集中會使用多位元組字元編碼,這些字元集要求用一個以上的位元組來唯一辨別每一個字元。比如,在日文shift-jis編碼中,就支援多位元組編碼,其最大的字元長度為2個位元組(一個起始位元組,一個結尾位元組)。

位元組類型 範圍

《Java安全編碼标準》一2.11 IDS10-J不要拆分兩種資料結構中的字元串

對結尾位元組而言,它可能覆寫單個位元組或者多位元組字元的起始位元組。當一個多位元組字元被分拆時,特别是跨不同的緩沖區邊界分拆時,它會産生不同的解釋,如果沒有按照正常的緩沖區邊界進行分拆的話。這種差異一般由構造字元時使用的位元組有二義性造成。

根據java api[api 2006]對character類的描述(unicode的字元表示):

char資料類型(和character對象封裝的值)需要依賴于初始的unicode編碼定義,其中将其定義為長度為16位的字元編碼。unicode編碼後來經過改動,允許使用多于16位來表示字元編碼。合法的字元編碼位于u0000?~u10ffff,這就是我們所熟悉的unicode字元編碼值。

java 2平台在char數組、string和stringbuffer?類中使用utf-16編碼表示。在這種字元表示中,補充字元會用一對char值來表示,第一個高位字元範圍是ud800~udbff,第二個低位字元範圍是udc00~udfff。

一個int值可以表示所有的unicode編碼字元,包括那些補充碼。int類型中的最低21位是用來表示unicode編碼的,其他的11個高位必須為0。除非特指,關于補充碼和字元值有下面的規則:

那些隻能接受char值的方法是不支援補充碼的。替代範圍内的char值會被認為是未定義的字元。比如,character.isletter('ud840')會傳回false,即使這種特殊字元緊跟着任何表示字母的字元串的低位替代值。

接受int值的方法支援所有的unicode字元,包括字元。比如,character.isletter(0x2f81a)會傳回true,因為這個碼點值代表一個字母(在cjk編碼中)。

這個不符合規則的代碼示例會從一個套接字中讀取1024個位元組,并且使用這些資料建立一個字元串。它同時使用一個while循環來讀取這些位元組,就像在fio10-j中推薦的那樣。當檢測到套接字中的資料多于1024個位元組時,它就會抛出異常。這樣的機制能夠防止非受信的輸入耗盡程式的記憶體。

這個代碼示例沒有考慮到使用多位元組編碼的字元和循環選代邊界之間的關系。如果在最後一個通過read()方法讀取的資料流中存在一個對位元組編碼的首位元組,那麼其他的位元組隻能在循環的下一次處理。然而,可以通過在一個循環中建立一個新的字元串來解決多位元組編碼問題。這樣的話,多位元組編碼就可能會被錯誤地解釋。

該符合規則的方案将字元串的建立推遲到接收完所有的資料時才完成。

這段代碼避免了将跨不同緩沖區的多位元組編碼字元分隔的問題,使用的方法是,直到讀取完所有的資料,才開始建立字元串。

這個符合規則的方案使用的是reader而不是inputstream。這個reader類會快速地将位元組資料轉換為字元資料,是以能夠避免分隔多位元組字元的問題。當套接字使用多于1024個字元而不是恰好使用1024位元組時,這個例程會自動退出。

這個不符合規則的代碼示例想截取字元串中的首字母。它的做法不對,因為使用了character.isletter()方法,這個方法不能處理補充字元和合并字元[hornig 2007]。

這個不符合規則的代碼示例想要糾正使用?string.codepointat()?的錯誤,這個方法使用了int類型作為輸入參數。這對補充字元來說是正确的,但對于合并字元而言卻是錯誤的[hornig 2007]。

這個方案可以處理補充字元和合并字元[hornig 2007]。根據java api[api 2006]文檔對java.text.breakiterator的說明:

breakiterator實作了能夠在文本邊界内定位的方法。breakiterator?的對象執行個體維護了目前的位置資訊,并且會對文本進行掃描,當遇到文本邊界的時候,它會傳回字元所在的位置索引。

傳回的邊界可能是那些補充字元、合并字元。例如,一個音節字元可以被存儲為一個基礎字元加上一個用來區分的符号。

如果要對locale敏感的字元串比較、搜尋和排序,可以使用?java.text.collator?類。

如果沒有考慮到補充字元和合并字元的話,将會導緻不可預計的行為。

《Java安全編碼标準》一2.11 IDS10-J不要拆分兩種資料結構中的字元串
《Java安全編碼标準》一2.11 IDS10-J不要拆分兩種資料結構中的字元串