本節書摘來異步社群《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攻擊。