DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 使用 XML: 安全編碼實踐 (1)
使用 XML: 安全編碼實踐 (1)
編輯:XML詳解     

過去七年來,作為一名顧問、培訓人員和作者,我有幸從一個特殊的角度見證了 XML 的發展和成熟。

  XML 最初出現的時候,組織和開發人員對這種新的“標記語言——不管它是什麼”彬彬有禮地保持懷疑態度。後來,隨著使用 XML 解決的問題越來越多,他們變得熱情起來。現在,很多開發人員和組織很自然地將 XML 納入他們的項目之中。

  不幸的是,應用的增加帶來了濫用,從這個意義上說,XML 也反映了其他技術的采用過程。任何新技術的第一批用戶通常都滿懷熱情(如果要讓同事和客戶承認這項技術的價值就必須如此),但是他們可能也有懷疑,因此通常會花費時間來研究如何最好地實現這種新技術。

  隨著技術的成熟,不斷地得到承認。當這項技術在越來越多的應用程序中使用時,出現的錯誤也越來越多。所幸的是,同時經驗也積累起來了:針對普遍問題經過測試的解決方案以及常見的陷阱出現並經過整理。

  為了撰寫這四篇文章,我翻閱了自己的筆記查找反復出現的 XML 陷阱。我希望將其整理出來並給出解決的方法,幫助您避免成為這一技術常見問題的犧牲品。

  首先從最基本的一層——XML 本身開始。堅持共同的語法是建立可靠應用程序的第一步。這一部分討論三個常見的問題:

  解析器和字符轉義的使用

  編碼

  名稱空間

  後續文章將探討如何可靠地使用 XML 文檔、如何驗證和測試 XML 文檔、如何建立 XML 和其他文件格式的接口,如圖像、視頻、字處理等等。

  文雅的語法

  第一節介紹 XML 語法的一些基本知識。如果對此已經非常精通,完全可以跳過這一節。

  XML 語法很簡單:最基本的一點就是開標簽和閉標簽必須匹配。但是我希望以後不會再受到這樣的電子郵件,“我嘗試用這樣那樣的工具處理附件中的 XML 文檔,但是行不通,還有其他的工具嗎?”毫無例外的,我打開文檔後總能找到明顯的語法錯誤,比如沒有反斜槓的空標簽(應該是這樣: <empty/>)。如果文檔不完全符合 XML 語法,就不是一個 XML 文檔;如果不是 XML 文檔,XML 工具就不能處理它。XML 有一種非常精確而正式的語法。一個文檔要麼完全符合語法,要麼不被看作是 XML 文檔。僅此而已。

  相反,有些應用程序可能不認可無安全有效的文檔。應用程序可能沒有完全實現該語法,因而無法識別,比方說字符實體(如 î)。

  XML 的問題時看起來太簡單。編寫某個東西常常看起來比學習另一個組件更容易、更快捷。在封閉的環境中,應用程序讀取自己生成的文檔,這種方法可能奏效,但是在多個應用程序使用文檔的產品環境不大可能。

  解決之道

  所幸的是,使用 XML 解析器很容易避免這種問題。每種編程語言都由適用的 XML 解析器(即使 Cobol 都有強大的 XML 支持),沒有理由不使用。

  作為開發人員,您有兩種選擇:XML 解析器或者編組組件。如果需要底層控制 XML 文檔的解碼,應該使用 XML 解析器。對於本文而言,解析器使用 DOM、JDOM、SAX 或 StAX 都沒有關系,但真正的 XML 解析器是正確讀取 XML 文檔的唯一保證。

  如果不需要更多地控制解析過程,可以找到更方便的編組組件,如 JAXB、Castor 或 Axis。編組組件直接在 XML 標簽和 Java™ 對象之間映射。 JAXB 和 Castor 是為了處理文件中的文檔設計的,Axis 用於 Web 服務。編組組件內嵌有 XML 解析器,可以確信它們完全實現了語法。

  雖然建議使用解析器讀取 XML 文檔,如果自己實現了寫文檔例程,也可以避開解析器。讀取 XML 文檔是一項復雜的任務,因為讀者必須支持完整的語法,但是寫 XML 文檔相對容易一些,因為可以避開語法的一個子集:如果不需要屬性,就不需要支持屬性;如果不需要多種編碼,就不需要支持多種編碼,依次類推。

  這裡唯一的陷阱是需要正確地轉義保留字符(參見表 1)。特別要注意實體字符(如 î),因為它們依賴於文檔編碼(請參閱“編碼的問題”)。

  表1. 保留

字符 轉義序列 說明 < <   & &   > >   ' ' 僅用於屬性,如果使用 " 作為分隔符 " " 僅用於屬性,如果使用 ' 作為分隔符 其他 &#unicode; 當前編碼中不支持的任何字符

  類似 清單 1 所示的一個簡單循環通常就足夠了。更有效的實現該功能是可能的,但如果寫入 UTF-8 或 UTF-16 流,清單 1 在語法上是有效的(否則,還需要將某些字符轉義成字符實體)。

  清單 1. 繁瑣的轉義實現

        // assumes UTF-8 or UTF-16 as encoding,
public String escape(String content)
{
  StringBuffer buffer = new StringBuffer();
  for(int i = 0;i &lt; content.length();i++)
  {
   char c = content.charAt(i);
   if(c == '&lt;')
     buffer.append("&amp;lt;");
   else if(c == '&gt;')
     buffer.append("&amp;gt;");
   else if(c == '&amp;')
     buffer.append("&amp;amp;");
   else if(c == '&quot;')
     buffer.append("&amp;quot;");
   else if(c == '&apos;')
     buffer.append("&amp;apos;");
   else
     buffer.append(c);
  }
  return buffer.toString();
}

  有些開發人員更喜歡 CDATA 節而非轉義。CDATA 是表明某一部分文檔可能包含非轉義保留字符的一種機制。比如,<condition><![CDATA[a > 4]]></condition>。本系列的第三篇文章中,我還將討論 CDATA 節,現在只要知道它們比轉義更安全就足夠了,因為 CDATA 節不能包含另一個 CDATA 節。

  更靈活的解決之道是求助於轉換程序,請參閱我的技巧文章“Implement XMLReader”(developerWorks)。

  其他方法

  如果必須和違反 XML 語法的應用程序打交道,又無法說服開發人員修改他/她的應用程序,怎麼辦呢?

  我發現,更好的辦法是認為這類應用程序根本不生成 XML,增加一個步驟將這種不正常的 XML 轉化成正確的 XML。為什麼要增加一個步驟呢?因為這樣可以隔離不符合標准的地方,能夠使用任何 XML 工具進行後續的處理。

  編碼的問題

  編碼的使用可能帶來更嚴重的問題。開發人員常常忽視了一個問題,編碼沒有限制 XML 支持的字符集。每個 XML 文檔都支持完整的 Unicode 字符集(XML 1.1 中是 16-bit 或 32-bit 字符)。

  對 XML 文檔編碼可以縮小文檔的大小,但是並沒有限制文檔使用特定的 Unicode 子集,這要感謝字符實體。事實上,通過字符實體,可以插入 Unicode 編碼表中的任何字符,即使文檔是用最嚴格的編碼(US-ASCII,只適合四種語言:English、Hawaiian、Latin 和 Swahili)。

  這是一個問題,因為 Java 應用程序或者最新版本的 DB2® 可能支持 Unicode,但是很少有遺留系統也支持 Unicode。因此如果 XML 流進入遺留應用程序,可能需要解決 Unicode 的問題。為了避免誤解,我們再說明一次,強制采用某種編碼不能解決問題,因為如上所述,可以將特殊字符轉義成字符實體。

  因為很少有可能重寫遺留系統,就需要某種轉換例程將 Unicode 字符轉化成應用程序能夠接受的字符集,比如將“î”轉化成直接的“i”(去掉抑揚符號)。多數 XML 解析器都提供了操縱 Unicode 字符的例程。

  名稱空間問題

  本文中第三個也是最後一個問題的來源是 XML 名稱空間。

  引入名稱空間是為了管理 XML 詞匯表,避免標簽同名。不同上下文中的兩個詞匯表常常使用同一個標簽。比如,消息詞匯表中可能包含 subject、date、from、to 和d body 這類標簽(參見 清單 2),而數字資產詞匯表可能包含 subject、date、description、camera 和 frame number 這類標簽(參見清單 3)。

  清單 2. 消息詞匯表

<envelope>
  <subject>Test memo</subject>
  <date>April 26, 2005</date>
  <from>[email protected]</from>
  <to>[email protected]</to>
  <body>memo body goes here</body>
</envelope>

  清單 3. 數字資產詞匯表

<photo>
  <subject>Westlicht Museum of Camera and Photography, VIEnna</subject>
  <date>April 25, 2005</date>
  <description>Lobby of the museum</description>
  <camera>Nikon D70</camera>
  <frame>5643</frame>
</photo>

  數字資產如果通過消息平台發送就會出現沖突,因為軟件混淆了兩個詞匯表中的 subject 和 date 標簽。換句話說,標簽名不是全局標識符。

  XML 名稱空間通過在標簽名前增加全局標識符將本地名轉化成全局名。為了保證全局標識符的唯一性,全局標識符必須是 URI(就是說很可能包含注冊的域名以保證唯一性)。結果如清單 4 所示。

  清單 4. 組合詞匯表

<env:envelope XMLns:env="http://psol.com/2005/env"
       XMLns:ph="http://psol.com/2005/photo">
  <env:subject>Latest photo</env:subject>
  <env:date>April 27, 2005</env:date>
  <env:from>[email protected]</env:from>
  <env:to>[email protected]</env:to>
  <env:body>
   <ph:photo>
     <ph:subject>Westlicht Museum
       of Camera and Photography, VIEnna</ph:subject>
     <ph:date>April 25, 2005</ph:date>
     <ph:description>Lobby of the museum</ph:description>
     <ph:camera>Nikon D70</ph:camera>
     <ph:frame>5643</ph:frame>
   </ph:photo></env:body>
</env:envelope>

  我來澄清常常被誤解的兩件事:

  URI 是標識符,不是地址。

  前綴不是標識符。

  URI 和地址

  雖然一般的 URI 是地址,但對於 XML 名稱空間來說,它們僅作為標識符來使用。。我希望名稱空間像 Java 包那樣來標識,但是不能這樣做——比如用 com.psol.vocabulary 代替更容易造成混淆的 http://psol.com/vocabulary。

  既然是標識符,這個地址就可能是無效的,就是說如果嘗試打開該地址就會返回“404 - Resource not found”錯誤。但它們仍然用於這個目的。和一般的想法相反,名稱空間 URI 並不指向 W3C XML Schema。

  其次,因為這個上下文中 URI 是標識符,應用程序必須逐字符地匹配該 URI。修改 XML 詞匯表的 URI,比方說讓它指向您的服務器,這種做法是錯誤的。比如,XSL 的 URI 是 http://www.w3.org/1999/XSL/Transform。如果您在BM®工作,也不能將其改為 http://www.ibm.com/1999/XSL/Transform。事實上,根本不能改變已有詞匯表的 URI。

  我在講授 XSLT 時,學生常常抱怨處理程序不工作,而實際上是他們沒有正確的復制 XSLT URI。

  結論之一是,應該避免修改名稱空間。在 URI 中包含版本模式通常不是一個好主意,這樣注定會讓應用程序無法工作(不錯,我承認 W3C 對 SOAP 是這樣做的)。

  前綴

  另一種常見的錯誤是混淆了前綴和標識符。標簽名不是標識符,同樣,前綴也不是標識符。兩個不同應用程序使用同一前綴的風險很大。因此,名稱空間前綴是透明的,不應該在應用程序中顯式地操縱前綴。但是,XML 作者在文檔中修改前綴是完全合理的(比如為了避免沖突)。

  因此應該避免清單 5 這樣的代碼, 而仿效清單 6 中的寫法。

  清單 5. 不正確的前綴測試

startElement(Stringuri,Stringlocal,Stringqname,Attributesatts)
{
  if(qname.equals("env:Envelope"))
   ;  // do something
}

  清單 6. 正確的測試名稱空間 URI

startElement(Stringuri,Stringlocal,Stringqname,Attributesatts)
{
  if(uri.equals("http://psol.com/2005/envelope")
   && local.equals("Envelope"))
   ;  // do something
}

  結束語

  只要記住這些陷阱,就可以在很大程度上改進您的 XML 編碼。更重要的是,這樣可以降低不兼容的危險,極大地簡化 XML 應用程序的維護。本系列的後續文章將考察和 XML 應用而非語法有關的常見陷阱。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved