//可以看出,base64 url 是標準base64編碼的乙個變種,分別用 -、_ 替換標準base64編碼結果中的 + 、 / ,並刪除結果最後的 = 。php版本
function base64_url_encode($data
) function base64_url_decode($data
)
本文是圍繞這兩個問題思考和實踐的結果。
我認為,理解base64或其他類似編碼的關鍵有兩點:
計算機最終儲存和執行的是01二進位制序列,這個二進位制序列的含義則由解碼程式/解釋程式決定
很多場景下的資料傳輸要求資料只能由簡單通用的字元組成,比如http協議要求請求的首行和請求頭都必須是ascii編碼
以青雲應用為例,簡單解釋這兩點。青雲平台通過post乙個表單來獲取iframe,表單有 payload 和 signature 兩項, payload 原本是乙個json物件,其中的鍵值可能包含一些特殊字元,比如 &、/ 等,由於青雲設計的一種通用的請求互動方案,需要考慮iframe服務方伺服器端的各種可能實現,有些伺服器端實現沒有考慮表單值有這些特殊字元,或者post請求被中間伺服器轉換成get請求再次發出,對於url來說,&、/都是具有特殊含義的字元,所以需要對請求資料進行特殊編碼避免這些字元出現 - 資料傳送方對資料按規則進行編碼,接收方對應地按規則解碼資料。
base64編碼之所以稱為base64,是因為其使用64個字元來對任意資料進行編碼,同理有base32、base16編碼。標準base64編碼使用的64個字元為:
這64個字元是各種字元編碼(比如ascii編碼)所使用字元的子集,基本,並且可列印。唯一有點特殊的是最後兩個字元,因對最後兩個字元的選擇不同,base64編碼又有很多變種,比如base64 url編碼。
base64編碼本質上是一種將二進位制資料轉成文字資料的方案。對於非二進位制資料,是先將其轉換成二進位制形式,然後每連續6位元(2的6次方=64)計算其十進位制值,根據該值在上面的索引表中找到對應的字元,最終得到乙個文字字串。
假設我們要對 hello! 進行base64編碼,按照ascii表,其轉換過程如下圖所示:
可知 hello! 的base64編碼結果為 s**sbg8h ,原始字串長度為6個字元,編碼後長度為8個字元,每3個原始字元經base64編碼成4個字元,編碼前後長度比4/3,這個長度比很重要 - 比原始字串長度短,則需要使用更大的編碼字符集,這並不我們想要的;長度比越大,則需要傳輸越多的字元,傳輸時間越長。base64應用廣泛的原因是在字符集大小與長度比之間取得乙個較好的平衡,適用於各種場景。
是不是覺得base64編碼原理很簡單?
但這裡需要注意乙個點:base64編碼是每3個原始字元編碼成4個字元,如果原始字串長度不能被3整除,那怎麼辦?使用0值來補充原始字串。
以 hello!! 為例,其轉換過程為:
注:圖表中藍色背景的二進位制0值是額外補充的。
hello!! base64編碼的結果為 s**sbg8hiqaa 。最後2個零值只是為了base64編碼而補充的,在原始字元中並沒有對應的字元,那麼base64編碼結果中的最後兩個字元 aa 實際不帶有效資訊,所以需要特殊處理,以免解碼錯誤。
標準base64編碼通常用 = 字元來替換最後的 a,即編碼結果為 s**sbg8hiq==。因為 = 字元並不在base64編碼索引表中,其意義在於結束符號,在base64解碼時遇到 = 時即可知道乙個base64編碼字串結束。
如果base64編碼字串不會相互拼接再傳輸,那麼最後的 = 也可以省略,解碼時如果發現base64編碼字串長度不能被4整除,則先補充 = 字元,再解碼即可。
解碼是對編碼的逆向操作,但注意一點:對於最後的兩個 = 字元,轉換成兩個 a 字元,再轉成對應的兩個6位元二進位制0值,接著轉成原始字元之前,需要將最後的兩個6位元二進位制0值丟棄,因為它們實際上不攜帶有效資訊。
為了理解base64編碼解碼過程,個人實現了乙個非常簡陋的base64編碼解碼程式,見:youngsterxyf/xiabase64。
由於base64應用廣泛,所以很多程式語言的標準庫都內建base64編碼解碼包,如:
本文開始提到的青雲應用例子只是base64編碼的應用場景之一。由於base64編碼在字符集大小與編碼後資料長度之間做了較好的平衡,以及base64編碼變種形式的多樣,使得base64編碼的應用場景非常廣泛。下面舉2個常用常見的例子。
但請注意:如果較大,的色彩層次比較豐富,則不適合使用這種方式,因為其base64編碼後的字串非常大,會明顯增大html頁面,影響載入速度。
我們的電子郵件系統,一般是使用smtp(簡單郵件傳輸協議)將郵件從客戶端發往伺服器端,郵件客戶端使用pop3(郵局協議,第3版本)或imap(互動郵件訪問協議)從伺服器端獲取郵件。
smtp協議一開始是基於純ascii文字的,對於二進位制檔案(比如郵件附件中的影象、聲音等)的處理並不好,所以後來新增mime標準來編碼二進位制檔案,使其能夠通過smtp協議傳輸。
舉例來說,我給自己發封郵件,正文為空,帶乙個名為hello.txt的附件,內容為 您好!世界!。匯出郵件原始碼,其關鍵部分如下圖所示:
mime-version: 1.0:表示當前使用mime標準1.0版本。
content-type: text/plain; name="hello.txt":表示附件檔名為 hello.txt ,格式為純文字。
content-transfer-encoding: base64:表示附件檔案內容使用base64編碼後傳輸。
5oko5aw977ym5liw55wm77yb:則是檔案內容 您好,世界! base64編碼後的結果。
不過,mime使用的不是標準base64編碼。
可能會有人在不理解base64編碼的情況下,將其誤用於資料加密或資料校驗。
base64是一種資料編碼方式,目的是讓資料符合傳輸協議的要求。標準base64編碼解碼無需額外資訊即完全可逆,即使你自己自定義字符集設計一種類base64的編碼方式用於資料加密,在多數場景下也較容易破解。
對於資料加密應該使用專門的目前還沒有有效方式快速破解的加密演算法。比如:對稱加密演算法aes-128-cbc,對稱加密需要金鑰,只要金鑰沒有洩露,通常難以破解;也可以使用非對稱加密演算法,如 rsa,利用極大整數因數分解的計算量極大這一特點,使得使用公鑰加密的資料,只有使用私鑰才能快速解密。
對於資料校驗,也應該使用專門的訊息認證碼生成演算法,如 hmac - 一種使用單向雜湊函式構造訊息認證碼的方法,其過程是不可逆的、唯一確定的,並且使用金鑰來生成認證碼,其目的是防止資料在傳輸過程中被篡改或偽造。將原始資料與認證碼一起傳輸,資料接收端將原始資料使用相同金鑰和相同演算法再次生成認證碼,與原有認證碼進行比對,校驗資料的合法性。
那麼針對各大**被脫庫的問題,請問應該怎麼儲存使用者的登入密碼?
答案是:在註冊時,根據使用者設定的登入密碼,生成其訊息認證碼,然後儲存使用者名稱和訊息認證碼,不儲存原始密碼。每次使用者登入時,根據登入密碼,生成訊息認證碼,與資料庫中儲存的訊息認證碼進行比對,以確認是否為有效使用者,這樣即使**被脫庫,使用者的原始密碼也不會洩露,不會為使用者使用的其他**帶來賬號風險。
當然,使用的訊息認證碼演算法其雜湊碰撞的概率應該極低才行,目前一般在hmac演算法中使用sha256。對於這種方式需要注意一點:防止使用者使用弱密碼,否則也可能會被暴力破解。現在的**一般要求使用者密碼6個字元以上,並且同時有數字和大小寫字母,甚至要求有特殊字元。
另外,也可以使用加入隨機salt的雜湊演算法來儲存校驗使用者密碼。這裡暫不細述。
base64兼顧字符集大小和編碼後資料長度,並且可以靈活替換字符集的最後兩個字元,以應對多樣的需求,使其適用場景非常廣泛。
當然,很多場景下有多種編碼方式可選擇,並非base64編碼不可,視需求,權衡利弊而定。
參考:base64編碼原理與應用
Base64編碼原理與應用
標準base64只有64個字元 英文大小寫 數字和 以及用作字尾等號 base64是把3個位元組變成4個可列印字元,所以base64編碼後的字串一定能被4整除 不算用作字尾的等號 等號一定用作字尾,且數目一定是0個 1個或2個。這是因為如果原文長度不能被3整除,base64要在後面新增 0湊齊3n位...
BASE64 編碼原理
unit ubase64 編碼原理 將3個位元組轉換成4個位元組 3 x 8 24 4x6 先讀入3個位元組,每讀乙個位元組,左移8位,再右移四次,每次6位,這樣就有4個 位元組了。解碼原理 將4個位元組轉換成3個位元組,先讀入4個6位 用或運算 每次左 移6位,再右移3次,每次8位,這樣就還原了。...
Base64編碼原理
一 base64編碼原理 1 base64編碼原理簡單介紹 base64要求把每三個8bit的位元組轉換為四個6bit的位元組 3 8 4 6 24 然後把6bit再添兩位高位0,組成四個8bit的位元組,也就是說,轉換後的字串理論上將要比原來的長1 3。編碼原理如下所示 1 base64的編碼都是...