你真的瞭解URL encode嗎?

  最近因項目須要,需重寫網絡組件。在重寫及review項目組的網絡組件舊代碼時,發現對URL編碼有不嚴謹之處。當說到這種寫法實際上是有問題時,幾個同事都表示很是詫異並幾度辯駁。本人表示有點當心驚,在網上搜索時還真的不多有另外的寫法。在此以本身的一些理解和經驗,作一下URL編碼的普及,但願對你們有所幫助,有問題也請不吝賜教。 (參考RFC1738,3986,6874,7320)web

  1、瞭解URL編碼以及編碼時機、運算過程瀏覽器

  URI包括URL和URN,經常使用說法URL encode實際是遵循URI的相關文件。在URI的最初設計時,但願能經過書面轉錄,好比寫在餐巾紙上告訴另一人,所以URI的構成字符必須是可寫的ASCII字符。在這些可書寫的字符裏,因爲一些字符在不一樣操做系統的編碼有不一樣的解析,被包含在Unsafe characters(圖3)之中,要格外注意。最後,在URI的構成字符中,最安全的方案是正確使用Reserved Characters (圖1)和 Unreserved Characters (圖1)的並集。
安全

  在對非法字符編碼到合法URI時,規定使用percent encode編碼,對非法字符的編碼結果爲三個字節(%+16進制字符*2)。然而如何生成percent編碼,沒有明確的指導規定,這也是大部分開發者拷貝舊代碼殊不知其因此然的緣由。服務器

  percent encode從字面上語義明確的指出其使用%作編碼標識,URL encode的實質就是正確的使用percent encode.網絡

  正確完成URL encode的關鍵問題在於:何時,對哪些內容,採用何種過濾原則,以及如何生成percent編碼?函數

  在WWW最初時,作法是將字符流轉換成字節流,按照ASCII字符與字節一一對應可相互轉換,使用對應ASCII字符的整型值做爲%的後兩個16進制字符,構成percent編碼。後來出現了多種percent編碼生成方法,致使了URI的難以識別。編碼

  現下作法,包括iOS使用percent相關的函數時,指定或系統默認的使用UTF8轉成字節流,每一個字節編成一個percent編碼,例如中文「網易」的URL編碼爲%e7%bd%91%e6%98%93,而其UTF8字節流爲e7 bd 91 e6 93,能夠看出其一一對應關係。url

  那麼percent編碼是在對非法字符采用某種編碼(約定爲UTF8)轉成字節流後,逐字節加上%構成percent編碼。spa

  因爲不一樣scheme或協議對URI格式有不一樣的要求,RFC關於對哪些內容編碼,採用何種過濾原則不作硬性規定。而將決定權延後到執行時由開發者根據須要決定。一般遵循如下原則:操作系統

  1.不要對Unreserved Characters作percent encode編碼。

  2.除了保留字符和非保留字符外的全部字符,必須使用percent encode進行編碼。

  3.保留字符不用於URI分隔符,而是用於其它位置,好比query部分的value時,要對這時用到的保留字符作percent encode編碼。

  4.當兩個URI的字符幾乎對等,區別只在於一個對某些字符用的原有字符,另外一個URI對這些字符作了percent encode時。絕大部分狀況下,這兩個URI應當被認爲是不一樣的兩個URI。所以,不該當對保留字在做爲保留字的使用場景時使用percent encode編碼

 

 圖1. 保留字和非保留字

圖2 不安全字符

  2、iOS開發中URL encode的方法編寫

  有三個函數或系統方法用於URL encode,CFURLCreateStringByAddingPercentEscapes(9.0廢棄),stringByAddingPercentEscapesUsingencode(9.0廢棄),

stringByAddingPercentencodeWithAllowedCharacters(系統推薦)。該系統推薦方法默認使用了UTF8編碼而後再根據咱們指定容許的字符集完成percent編碼。一般咱們在拼接GET請求URL時使用URL encode。

  應用場景通常是傳入URLString和一個參數NSDictionary, 這時須要傳入方保證URLString是已正確編碼的,而後遍歷NSDictionary的key和value, 按需指定容許的字符集對key值和value作編碼。結果輸出爲

  URLString[& | ?] URLencode(key1)=URLencode(value1)&URLencode(key2)=URLencode(value2)….
   筆者使用的容許字符集爲Unreserved Characters, 包括Reserved Characters以及中文等非法字符均會被percent編碼,示例方法以下
 
+ (NSURL *) createGETURLFromString:(NSString *)urlString params:(NSDictionary *)params {
    NSURL *parsedURL = [NSURL URLWithString:urlString];
    
    NSString* queryPrefix = parsedURL.query ? @"&" : @"?";
    NSMutableArray* pairs = [NSMutableArray array];
    for (NSString* key in [params keyEnumerator]) {
        if (![[params objectForKey:key] isKindOfClass:[NSString class]]) {
            continue;
        }
        NSString *value = (NSString *)[params objectForKey:key];
        
        NSCharacterSet *allowedCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"];
        
        NSString *urlEncodingKey = [key stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        NSString *urlEncodingValue = [value stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [pairs addObject:[NSString stringWithFormat:@"%@=%@", urlEncodingKey, urlEncodingValue]];
    }
    NSString* query = [pairs componentsJoinedByString:@"&"];
    return [NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@", urlString, queryPrefix, query]];
}

 

   3、URL encode中的 + 與 空格
  在使用過base64編碼的童鞋多半會知道,基礎base64衍生了web safe base64,更改了編碼字符集,其中基本表中會出現+和/字符,這個通常會被瀏覽器理解成空格和路徑分割符。因此爲了讓其工做正常,須要把索引表的最後兩個字符+和/分別替換成點  . 和下劃線  。+號通過percent編碼後爲%20,而20正是空格的ASCII碼,這大概是瀏覽器的設計者將+理解成空格的緣由。
  那麼在URL encode中是否是須要針對性的指定許可字符集,對+號和空格作處理防止混淆呢。實際上咱們在以前使用Unreserved Characters對內容作編碼時,並無容許+和/符號出如今內容項的編碼結果中。
  所以,正確的使用percent編碼,當傳入的參數字典中包含有+號和/字符也是能夠放心的。在傳入參數是base64的結果時,並不須要特別的將base64換成web safe base64。
 
  四 、不正確的URL encode可能致使的問題

   有的iOS開發者拿到  GET請求URLString和參數字典後,先拼接參數,而後再對整個字符串作URL encode,形成不能區分某些字符是處於分割組件的做用,或者是做爲組件的content。這是千萬不可取的,可能發生以下問題:

  1.若是沒有正確過濾,好比http://www.baidu.com作編碼後變成了http%3a%2f%2fwww.baidu.com%2findex.htm,將不能正常訪問。 也就是說,爲了支持對拼接後的字符串做URL encode, 必須對整個拼接後的字符串禁止對全部URI的保留字做編碼,好比&字符,這就形成了問題2和3。

  2. 當構建參數傳入{「name」 : 」namepart1&namepart2」,「id」 : "kk"}。此時拼接字符串編成了http://www.baidu.com?name=namepart1&namepart2&id=kk,那麼如何解析獲得"name"字段「namepart1&namepart2」的實際value值,以及id字段的值"kk"?
 
  3. 當構建參數傳入{「name」 : 」Mitty&isLogin=true」}。此時拼接字符串編成了http://www.baidu.com?name=Mitty&isLogin=true,若是isLogin真的是有意義的queryKey時,直接形成服務器接收了額外的參數。固然關於URL的攻擊有不少,好比semantic attack, 這裏不作討論。
 
  另外,有童鞋擔憂非法字符,先對中文作base64再放到get參數,從擔憂非法字符來說不是必要的,通過URL編碼後,與base64的結果一樣是ASCII字符集,在網絡上是能夠正常不丟失信息的傳輸的。 服務器接到請求時, 以PHP爲例,應針對每一個$_GET[「key」]作URL作解碼。
相關文章
相關標籤/搜索