2010-05-08 55 views
25

我正在爲空閒時間正在編寫的聊天應用程序編寫JS,並且需要根據用戶提交的數據更改HTML標識。通常情況下,這通常是一些不穩定的事情,我甚至不會去嘗試,但是這次我沒有看到自己有太多的選擇。然後我需要做的就是轉義HTML標識以確保它不會允許XSS或破壞HTML。在將用戶輸入添加到使用Javascript的DOM中之前對用戶輸入進行消毒

下面的代碼:

var user_id = escape(id) 
var txt = '<div class="chut">'+ 
      '<div class="log" id="chut_'+user_id+'"></div>'+ 
      '<textarea id="chut_'+user_id+'_msg"></textarea>'+ 
      '<label for="chut_'+user_id+'_to">To:</label>'+ 
      '<input type="text" id="chut_'+user_id+'_to" value='+user_id+' readonly="readonly" />'+ 
      '<input type="submit" id="chut_'+user_id+'_send" value="Message"/>'+ 
      '</div>'; 

什麼是逃避id避免上述任何一種問題的最好方法是什麼?正如你所看到的,現在我正在使用內置的escape()函數,但我不確定這應該與其他替代方法相比有多好。我大多習慣於在輸入到文本節點之前對輸入進行清理,而不是一個id本身。

+0

到底是什麼'id'的功能? – Tgr 2010-05-08 13:17:00

+0

現在id是代表用戶的任何字符串。我用它來區分屬於不同用戶的頁面的類似結構。 通過這個邏輯,MD5或base64可能是一個很好的等待,我想。這只是沒有內置的東西。 – 2010-05-08 13:40:47

回答

30

從不使用escape()。這與HTML編碼無關。這更像URL編碼,但它甚至不適合。這是一個奇怪的非標準編碼,只能在JavaScript中使用。

如果你想要一個HTML編碼器,你必須自己編寫它,因爲JavaScript不會給你一個。例如:

function encodeHTML(s) { 
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;'); 
} 

不過雖然這是足以讓你在user_idinput value地方,這是不夠的,因爲id的ID只能使用有限的字符選擇。 (和%不在其中,因此escape()甚至encodeURIComponent()是沒有好處的。)

你可以發明自己的編碼方案把任何字符的ID,例如:

function encodeID(s) { 
    if (s==='') return '_'; 
    return s.replace(/[^a-zA-Z0-9.-]/g, function(match) { 
     return '_'+match[0].charCodeAt(0).toString(16)+'_'; 
    }); 
} 

但你如果user_id發生兩次,仍然有問題。說實話,扔掉HTML字符串的整個過程通常是一個糟糕的主意。改爲使用DOM方法,並保留對每個元素的JavaScript引用,因此您不必一直調用getElementById,或者擔心如何將任意字符串插入到ID中。

例如。:

function addChut(user_id) { 
    var log= document.createElement('div'); 
    log.className= 'log'; 
    var textarea= document.createElement('textarea'); 
    var input= document.createElement('input'); 
    input.value= user_id; 
    input.readonly= True; 
    var button= document.createElement('input'); 
    button.type= 'button'; 
    button.value= 'Message'; 

    var chut= document.createElement('div'); 
    chut.className= 'chut'; 
    chut.appendChild(log); 
    chut.appendChild(textarea); 
    chut.appendChild(input); 
    chut.appendChild(button); 
    document.getElementById('chuts').appendChild(chut); 

    button.onclick= function() { 
     alert('Send '+textarea.value+' to '+user_id); 
    }; 

    return chut; 
} 

您也可以使用便捷函數或JS框架來減少創建集附加調用的長度。

ETA:

我使用jQuery的那一刻作爲一個框架

OK,然後再考慮了jQuery 1.4創建的快捷方式,例如:

var log= $('<div>', {className: 'log'}); 
var input= $('<input>', {readOnly: true, val: user_id}); 
... 

我現在的問題是,我使用JSONP添加元素和事件到一個頁面,所以我不知道這些元素是否已經存在或不存在之前顯示一條消息。

可以保持user_id至元素節點(或包裝對象)在JavaScript中查找,保存把這些信息在DOM本身,可以在id去字符的限制。

var chut_lookup= {}; 
... 

function getChut(user_id) { 
    var key= '_map_'+user_id; 
    if (key in chut_lookup) 
     return chut_lookup[key]; 
    return chut_lookup[key]= addChut(user_id); 
} 

(該_map_前綴是因爲JavaScript對象不相當工作作爲任意字符串的映射。空字符串,在IE瀏覽器,一些Object成員名稱,混淆。)

+0

我目前使用jQuery作爲框架,所以任何與此相關的想法都可能會有幫助。 我現在遇到的問題是,我使用JSONP將元素和事件添加到頁面,因此在顯示消息之前無法確定元素是否已存在。這讓我覺得我不得不使用糟糕的方法,我必須找到要選擇的元素,或者如果它們不在那裏,請添加它們。因此,我不認爲你最後的建議可行,但我可能是錯的。 受限制的字符集使我認爲該ID的MD5可能是我所能做的。 – 2010-05-08 14:26:42

+0

儘管我可以添加關於用戶名接受的字符的假設,但可以使用正則表達式並完成它。 – 2010-05-08 14:34:44

7

你可以使用一個簡單的正則表達式斷言ID只包含允許的字符,像這樣:

if(id.match(/^[0-9a-zA-Z]{1,16}$/)){ 
    //The id is fine 
} 
else{ 
    //The id is illegal 
} 

我的例子只允許字母數字字符,長度爲1的串16,你應該改變它以匹配您使用的ids類型。

順便說一下,在第6行,value屬性缺少一對引號,這是在兩個級別引用時容易犯的錯誤。

我看不到您的實際數據流,取決於上下文,可能根本不需要此檢查,或者它可能不夠。爲了做出適當的安全審查,我們需要更多的信息。

一般而言,關於建立在逃生或消毒功能,不要盲目信任他們。你需要確切地知道他們做了什麼,並且你需要確定這實際上是你需要的。如果它不是你所需要的,那麼你的代碼就是你自己的,大多數情況下,像我給你的簡單的白名單正則表達式工作得很好。

1

在HTML屬性中使用用戶提供的數據時,您需要採取額外的預防措施。因爲屬性比HTML標籤中的輸出具有更多的攻擊向量。

避免XSS攻擊的唯一方法是對除字母數字字符以外的所有內容進行編碼。使用& #xHH轉義ASCII值小於256的所有字符;格式。如果您使用CSS類和JavaScript來獲取這些元素,那麼不幸的是可能會在您的方案中導致問題。

OWASP有HTML屬性XSS如何減輕一個很好的說明:

http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.233_-_JavaScript_Escape_Before_Inserting_Untrusted_Data_into_HTML_JavaScript_Data_Values

12

另一種方法,我喜歡的是使用原生DOM功能:http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript

+1

@BrandonMintern的要點不適合我。 – cmcculloh 2016-07-05 18:10:12

+1

我寫了那篇博文。不幸的是,頂部給出的TL; DR技術不適用於HTML屬性。還有一些其他方法可以在文章結尾正確轉義HTML屬性,即:http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/#hack-3-more-efficient -catchall – 2016-07-06 23:06:59

1

既然你是文本轉義將出現在HTML屬性中,您必須確保不僅逃脫HTML實體,還轉義HTML屬性:

var ESC_MAP = { 
    '&': '&amp;', 
    '<': '&lt;', 
    '>': '&gt;', 
    '"': '&quot;', 
    "'": '&#39;' 
}; 

function escapeHTML(s, forAttribute) { 
    return s.replace(forAttribute ? /[&<>'"]/g : /[&<>]/g, function(c) { 
     return ESC_MAP[c]; 
    }); 
} 

然後,您的轉義碼變爲var user_id = escapeHTML(id, true)

欲瞭解更多信息,請參閱Foolproof HTML escaping in Javascript