談Web前端安全編碼
最近開發中涉及到有關輸出正確的HTML標簽這樣的問題,正好對字符編碼這塊兒多看看,之前對這個方面認識的不深,思考的確實不夠,如果下次再碰見類似的問題,若再次不少時間去調研的花,就得不償失了。
就像正則表達式一樣,似乎你知道它,但是每一次開發都需要現取查、現取測試,估計你會像我一樣,每一次都會花些意想不到的時間。
總之,重在學習。
有一篇文章,這個博友已經總結的非常到位了,引入于此,看到的同學,希望可以多看看他做的總結:
在web的開發的開發過程中,前端總是在處理后端打的各種變量,變量可以包含著中的各種各樣的字符,如果不對這些字符進行”特殊“處理的 話,輕者導致頁面不正常的顯示,潛入了其他的東西,亦即頁面掛了,或者彈出不應該彈出的東西,這些都是我們不期望看到的,重者可能導致密碼泄露,網站的訪 問量突然猛增,服務器掛掉。
在前端的開發中,涉及到以下幾種語境:
1)直接顯示在頁面上, eg:<div>{%username%}</div>,<input type="text" value="{%username%}"/>
2)在script 標簽中,eg :<script>var test = '{%username%}';var test="{%username%}"</script>
3)在頁面事件中,eg:<div onclick="alert('{%username%}')">334455</div>
4)在innerHTML 的語境中,eg:<div id="test"></div> <script>var test="{%username%}";document.getElementById('test').innerHTML =test; </script>
5)在頁面鏈接的url中:eg:<a href="{%username%}"></a>
6)提交url參數處理
7)js 獲取url參數值的時候
下面來一個一個的分析上面提到了7種語境中的轉義情況:
1)直接顯示在頁面上(簡稱頁面html環境中):
為了保證用戶的本意,完完全全的展示在頁面上,這類主要是防止標簽的自閉合,屬性中的單引號,雙引號已經存在的情況下不正確顯示,所以必須轉義4個字符:<,>,",' to為轉義的意思(下同)
(1)< to <
(2) > to >
(3)" to ";
(4)' to ';
2)在script的標簽中(簡稱js環境中):
在javascript 中 ” 和' 都是表示字符串,沒有任何區別,所以如果變量中出現了這2個字符,就會影響后面不正確顯示,所以必須轉義這2個字符 ,同理,如果變量中包含\ 會將后面的'或者“給轉義掉,
變成真正的’和”,也沒有閉合,導致語法錯誤,所以這個字符也需要轉義,另外在我們的注釋中存在/* */ 這種形式,如果在變量中出現了 */這種字符,就會將注釋掉的部分代碼給暴漏出來,所以也要轉義/字符,
綜上所述,在script標簽中要轉義的字符為:
(1)' to \'
(2)" to \"
(3)\ to \\
(4)/ to \/
3)在頁面事件中:
這類語境涉及到了頁面html和Js 環境,要執行什么轉義呢?到底是先html 在js,還是先js 再html 轉義呢?我們來看一個例子:
eg:<div onclick="alert('{%username%}')"></div>
當username = " 的時候,如果是先html ,然后再js 轉義的時候,那么就是<div onlick="alert('&qout;')"></div> 我們拿到頁面上去執行,發現語法報錯
如果是先js,先后在html轉義的時候,那么就是<div onlick="alert('\&qout;')"></div> 我們拿到頁面上去執行,成功!!
所以結論是 先進行JS 轉義,然后再進行html 轉義,為什么是這樣呢?因為這里它是要執行一個js函數的,如果都當做html來解析了,這里的js函數就不會執行,也就沒有js 環境的意思了。
綜上所述,在這累語境中需要轉義的字符為:
(1)' to \&
(2)" to \"
(3)\ to \\
(4)/ to \/
(5)< to <
(6)< to >
4) 在innerHTML環境中:
這類語境首先是js環境中,其次是在html環境中,顯然,先進行js轉義,然后再進行html轉義,需要轉義的字符同上述3)
5)在頁面鏈接的url 環境中:
這類比較復雜,url中本身涉及到很多的特殊字符,此外也會涉及到html 和js 環境中的賦值的情況, 特別注意,url 編碼和html的編碼是不一樣的,見后文附錄url編碼表和html編碼表
在html 和js環境中,需要轉義的字符為: ” ,' ,<,>,\ ,/
在其他環境中,需要轉義的字符為:+,空格,?,=,&,#, %
這類字符的轉義如下:
(1)" to %22;
(2)' to %22;
(3) < to %3C
(4) > to %3E
(5) \ to %5C
(6) / to %2F
(7) + to %2b
(8)空格 to %20
(9)? to %3F
(10)= to %3D
(11)& to %26
(12)# to 23
(13) % to %25
為什么要轉義這些字符呢?稍微web開發的經驗同學就知道,原因很簡單,如果存在這些字符的話,不進行轉義,那么我們就會得不到我們應該得到的東西
引申一點:在我們拼接url的時候,比如將表單中的數據提取出來,用ajax的方式提交的時候,也需要對上述字符進行轉義,不然得到的也不是想到的東西
6)提交url 參數的處理:
1) Form 表單提交方式:不需要做任何處理,表單會依照頁面的編碼進行編碼
2) ajax 的提交:因為ajax的提交的時候,是拼接成url的方式提交給后端的,所以必須要考慮對 +,空格,?,=,&,#, % 的轉義,通常使用 encodeURIComponent進行轉義
關于escape,encodeURI,encodeURIComponent 這三個函數的需要的轉義字符,見后面的附件列表
7) js 獲取url參數的值的時候
(1) 得到url中的參數值的時候,首先必須要進行unescape的轉碼才能使用,因為url中的一些特殊字符都經過了編碼
(2) 將url的值設置到一些參數上時,比如隱藏表單上的value值的時候(作提交refer),需要進行escape 編碼
附錄:
1)html 編碼: http://wenku.baidu.com/view/0dbaa1dc7f1922791688e8a2.html
2)url 編碼: http://baike.baidu.com/view/204662.htm
3)escape,encodeURI,encodeURIComponent 的區別: http://www.alixixi.com/web/a/2008081147930.shtml
上述引文來自: web前端安全編碼(模版篇)
我概要一下,分為兩類,模板(或稱為顯示)相關,另一類是URL相關;
一、模板(顯示)相關:
我們要把不確定性的后端文本變量(當然來自絕大部分來自用戶的輸入),為了讓之正確的顯示在頁面中(也就是HTML中),或者正確的執行js代碼,我們要對特殊字符進行轉義:
HTML環境:
< 轉義 < > 轉義 > " 轉義 " ' 轉義 '
JS環境:
' 轉義 \' " 轉義 \" \ 轉義 \\ / 轉義 \/
對應的工具函數如:
// 轉義為HTML環境 // 主要用于innerHTML這種場景 var toSwitchForHtml = function(text){ if (typeof text !== 'string') { throw new Error('The text must be a string !'); } return text .replace(/</g,'<') .replace(/>/g,'>') .replace(/"/g, """) .replace(/'/g, "'"); }; // 轉義為JS環境 // 主要用于執行js代碼,如new Function(someStringFunctionFromServer);等 var toSwitchForJs = function(text) { if (typeof text !== 'string') { throw new Error('The text must be a string !'); } return text .replace(/\\/g,'\\') .replace(/\//g,'\/') .replace(/"/g, """) .replace(/'/g, "'"); };
二、URL相關:
主要涉及3個可以對字符串編碼的函數,分別是:escape,encodeURI,encodeURIComponent,相應3個解碼函數:unescape,decodeURI,decodeURIComponent 。
相應的函數介紹,主要查看js api文檔即可。
這里做了個統計表格
方法 | 編碼范圍 | 作用 | 備注 |
escape | 不編碼: ASCII字母和數字、*+-./@_ 其余全部編碼為十六進制轉義序列 |
在所有的計算機上讀取該字符串。要注意,它是將需要轉義的字符轉義成UTF-16(因為JavaScript只支持16位UTF-16編碼)碼點。 | 關于這個轉義之后的unicode編碼方案,可參考: Unicode與JavaScript詳解 |
encodeURI | 不編碼: ASCII字母和數字、!’()*._~ URL特定標示符:;/?:@&=+$,# 其余全部編碼,根據URL編碼規則進行編碼 |
對整個URL進行編碼,而URL的特定標識符不會被轉碼。 | url編碼的含義: url編碼就是一個字符ascii碼的十六進制。不過稍微有些變動,需要在前面加上“%”。比如“\”,它的ascii碼是92,92的十六進制是5c, 所以“\”的url編碼就是%5c。那么漢字的url編碼呢?很簡單,看例子:“胡”的ascii碼是-17670,十六進制是BAFA,url編碼是“ %BA%FA”。更多請參看: URL編碼 |
encodeURIComponent | 不編碼: ASCII字母和數字、!’()*._~ 編碼: URL特定標示符:;/?:@&=+$,# 其余全部編碼,根據URL編碼規則進行編碼 |
將文本字符串編碼為一個統一資源標識符 (URI) 的一個有效組件。 | 請注意 encodeURIComponent() 函數 與 encodeURI() 函數的區別之處,前者假定它的參數是 URI 的一部分(比如協議、主機名、路徑或查詢字符串)。因此 encodeURIComponent() 函數將轉義用于分隔 URI 各個部分的標點符號。 |
根據上面的表格,我們再來分析使用場景:
1. 當js使用數據時可以使用escape,比如獲取或者設置URL中的參數值的時候(當然,約定該URL中傳遞的參數是經過unicode編碼的)
2. 進行 url跳轉 時可以整體使用encodeURI。如——
document.write(encodeURI("http://abc.com/do?name=文章&name=king"));
3. 傳遞參數 時需要使用encodeURIComponent,這樣組合的url才不會被#等特殊字符截斷。如——
<script >document.write('<a );</script>
弄清escape、encodeURI、encodeURIComponent的內容,量并不小,需要耐心去理解、去實踐。
參考:
2. escape()、encodeURI()、encodeURIComponent()區別詳解
3. URL編碼
4. HTML轉義字符