周三凌晨?jī)牲c(diǎn),一個(gè)后端工程師盯著日志里被攔腰截?cái)嗟牟樵冏址磸?fù)檢查了三次OAuth配置,最后發(fā)現(xiàn)問題出在一個(gè)前端同事隨手寫的encodeURI上——他傳進(jìn)來的&沒有變成%26,直接把一條搜索請(qǐng)求拆成了兩個(gè)參數(shù)。這件事后來被寫進(jìn)團(tuán)隊(duì)Wiki,標(biāo)題就叫“永遠(yuǎn)別用encodeURI拼查詢參數(shù)”。
這就是URL編碼里最容易踩的坑。表面上encodeURI和encodeURIComponent都是把特殊字符轉(zhuǎn)成%加兩位十六進(jìn)制碼,但它們的“手下留情”完全不同。encodeURI會(huì)把?、&、=、#、/這些對(duì)URL有結(jié)構(gòu)意義的字符原樣保留,它假設(shè)你手里是一個(gè)完整的地址,只需要把路徑或域名里礙事的空格、中文編碼一下。所以當(dāng)有人把"hello world"傳給encodeURI,它只會(huì)把空格變成%20,其余結(jié)構(gòu)不動(dòng),整條URL依然是合法的。
但一旦你要拼接查詢參數(shù),這個(gè)“手下留情”就變成了漏洞。假設(shè)用戶名是“John & Jane”,如果寫法是`https://api.example.com?name=${encodeURI(name)}`,那個(gè)&會(huì)赤裸裸地留在URL里,服務(wù)器解析時(shí)看到的是name=John%20,然后多了一個(gè)名為%20Jane的空參數(shù)。用encodeURIComponent則完全不同——它會(huì)連&也一起編碼成%26,出來的結(jié)果是name=John%20%26%20Jane,整個(gè)值是一個(gè)整體。encodeURIComponent的編碼字典沒有例外,包括?、=、#、/全部轉(zhuǎn)義,它默認(rèn)你要編碼的只是一小段放進(jìn)URL的數(shù)據(jù)片段,而不是一整條地址。
這種差異的根源在于兩種函數(shù)對(duì)“保留字符”的定義。encodeURI的不編碼清單除了字母數(shù)字和_-~.!~*'()這些安全符,還放行了;,/?:@&=+$#,幾乎把URL分隔符全部放生;encodeURIComponent只放過字母數(shù)字和_-~.!~*'(),連分號(hào)都不留。可以這么記:用encodeURI擦地,只拖水漬;用encodeURIComponent是搬家打包,所有東西都用泡沫紙裹好。所以官方給出的黃金法則只有一條——拼接查詢字符串時(shí),每個(gè)參數(shù)值必須過一遍encodeURIComponent,絕不能用encodeURI或裸傳用戶輸入。
實(shí)際開發(fā)里,最簡(jiǎn)單的安全范式就是用URLSearchParams。比如`new URLSearchParams({q: query, cat: category, page: page})`,它會(huì)在內(nèi)部自動(dòng)把所有值做encodeURIComponent處理,然后拼成完整的query string。這樣既不會(huì)漏編碼,也避免了手動(dòng)拼字符串時(shí)寫錯(cuò)變量名。如果必須手寫模板字符串,記得在每個(gè)變量外面套上encodeURIComponent,而且別嫌麻煩,哪怕變量看起來是純數(shù)字也套一下——未來某天它變成字符串時(shí)你不會(huì)回來改。
說到底,這兩個(gè)函數(shù)不是哪個(gè)更好,而是適用場(chǎng)景截然不同。encodeURI對(duì)付整條URL里需要轉(zhuǎn)義的那部分,encodeURIComponent專門對(duì)付嵌進(jìn)URL里的一段數(shù)據(jù)。搞混了,不會(huì)立即報(bào)錯(cuò),但會(huì)在某個(gè)午夜的生產(chǎn)環(huán)境里突然炸開——就像那個(gè)工程師學(xué)到的那樣。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.