天天看點

《Java編碼指南:編寫安全可靠程式的75條建議》—— 指南6:正确地編碼或轉義輸出

本節書摘來異步社群《java編碼指南:編寫安全可靠程式的75條建議》一書中的第1章,第1.6節,作者:【美】fred long(弗雷德•朗), dhruv mohindra(德魯•莫欣達), robert c.seacord(羅伯特 c.西科德), dean f.sutherland(迪恩 f.薩瑟蘭), david svoboda(大衛•斯沃博達),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

适當的輸入檢查可以防止惡意資料插入資料庫等子系統。雖然不同的子系統需要不同類型的資料無害化處理,但是子系統最終要接收的輸入形式都很明确,是以可以很清楚地知道需要什麼樣的資料無害化處理。

有幾個用于輸出資料的子系統。html渲染器是一種常見的用于顯示程式輸出的子系統。發送給輸出子系統的資料,來源似乎都很可靠。然而,假設輸出資料不必做無害化處理,是很危險的,因為這些資料可能間接來源于一個不可信的來源,并且可能包含惡意的内容。如果沒能正确地處理傳遞給輸出子系統的資料,就會讓多種類型的攻擊有機可乘。例如,html渲染器易受html注入攻擊和跨站腳本(xss)攻擊[owasp 2011]。是以,用于防止此類攻擊的輸出無害化處理,和輸入無害化處理一樣重要。

和輸入驗證一樣,資料應該在消除惡意字元之前被标準化。正确編碼所有輸出字元,其中那些已知的、不會由于繞過資料驗證而導緻安全漏洞的字元除外。更多資訊參見《the cert® oracle® secure coding standard for java™》[long 2012]的“ids01-j. normalize strings before validating them”。

下面的違規代碼示例使用基于java ee的spring架構中的模型-視圖-控制器(model-view-controller,mvc)概念,向使用者顯示了沒有經過編碼或轉義的資料。因為資料會被發送到web浏覽器,是以該代碼容易受到html注入攻擊和xss攻擊。

public class validateoutput {

 // allows only alphanumeric characters and spaces

 private static final pattern pattern =

  pattern.compile("^[a-za-z0-9\s]{0,20}$");

 // validates and encodes the input field based on a whitelist

 public string validate(string name, string input)

   throws validationexception {

  string canonical = normalize(input);

  if (!pattern.matcher(canonical).matches()) {

   throw new validationexception("improper format in " +

                  name + " field");

  }

  // performs output encoding for nonvalid characters

  canonical = htmlentityencode(canonical);

  return canonical;

 }

 // normalizes to known instances

 private string normalize(string input) {

  string canonical =

   java.text.normalizer.normalize(input,

    normalizer.form.nfkc);

 // encodes nonvalid data

 private static string htmlentityencode(string input) {

  stringbuffer sb = new stringbuffer();

  for (int i = 0; i < input.length(); i++) {

   char ch = input.charat(i);

   if (character.isletterordigit(ch) ||

      character.iswhitespace(ch)) {

     sb.append(ch);

    } else {

     sb.append("" + (int)ch + ";");

    }

   }

   return sb.tostring();

}

// ...

@requestmapping("/getnotifications.htm")

public modelandview getnotifications(httpservletrequest request,

  httpservletresponse response) {

 validateoutput vo = new validateoutput();

 modelandview mv = new modelandview(); 

 try {

  userinfo userdetails = getuserinfo();

  list> list =

   new arraylist>();

  list notificationlist =

   notificationservice.getnotificationsforuserid(

    serdetails.getpersonid());

  for (notification notification: notificationlist) {

    map map = new hashmap();

    map.put("id", vo.validate("id" ,notification.getid()));

    map.put("message",

     vo.validate("message", notification.getmessage()));

    list.add(map);

  mv.addobject("notifications", list);

 catch (throwable t) {

  // log to file and handle

 return mv;

}<code>`</code>

當接受危險的字元如雙引号和尖括号時,必須對輸出進行編碼和轉義。即使在輸入白名單中不允許出現這樣的字元,也要對輸出進行轉義,因為這樣就提供了一個二級防禦。注意,确切的轉義序列會發生變化,具體取決于該輸出将要被嵌入的地方。例如,html标簽屬性值、css、url或者腳本中都可能會出現不可信輸出,不同情況下的輸出編碼例程也會有所不同。另外,在有些上下文中,無法安全地使用不可信的資料。

在輸出被顯示前或被傳遞到可信邊界前,沒能對其進行編碼或轉義,導緻任意代碼的執行。

據2006年1月報道,apache geronimo-1474漏洞允許攻擊者送出包含javascript的url。網絡通路日志檢視器(web access log viewer)未能對跳轉到管理者控制台的資料進行無害化處理,進而促成了一個典型的xss攻擊。