Featured image of post base64

base64

沒想到第一篇技術分享文章會先從這個小主題開始。主要原因是之前在面試一家非常喜歡的公司,其中一個面試關卡因為沒有考慮到問題條件限制之一是 input 資料為以 base64 編譯後的資料,需要先用程式 decode 為 binary 再做後續處理,最後很可惜沒有進入下一關。後來回想一下,其實我在實習的時候就有碰過這個東西了,只不過當時做 base64 encode 我都直接把 service 密碼明文丟到 google 上的 “online base64 encoder” 直接做好 encode 後的 string🫠,算是根本沒懂過這個概念。所以決定針對他好好來研究一下,並記錄自己的研究內容~

base64 的基本概念

base64 是一種數據格式轉換方法,基本上 base64 encoding 的輸入資料會是一組二進制(binary)編碼,之後依照某種轉換規則,將一組組的 binary 數據對應到特定的 base64 character,最後再輸出一個 text form 的 output。

為什麼要做 base64 轉換?

主要原因大概如下:

  1. 提升安全性

    有時候一些人類很直觀能理解的密碼明文,比如 「elaine_hsieh」,如果直接撰寫在 service configuration 上,會讓取得密碼的過程太過容易。如果將這樣的資訊包上一層 base64 encoding、變成「ZWxhaW5lX2hzaWVo」,之後有人想要取得 service 密碼將需要多一道解譯的手續。因此,透過 base64 encoding 可以進而提升一點資訊安全性。

    以 base64 編碼還有個額外好處:便於不同編碼方式之間進行數據轉換後的數據傳輸。

    通常某個 service 密碼明文會使用 ASCII 或是 UTF-8 的編碼方式。使用 ASCII 的明文包含範圍較小,通常僅包含 256 個基本的字元,例如英文字母、數字、標點符號(此處指的是extended ASCII);使用 UTF-8 的明文包含範圍較大,例如英文字母、數字、標點符號、拉丁字母、中日韓等亞洲文字、表情符號與其他符號。由於 base64 character set包含了部分 ASCII character set,所以不論原始數據是以 ASCII做編碼(例:titan in ASCII code)、或是以 UTF-8做編碼(例:titan in UTF-8 code),都可以透過 base64 轉換規則轉換為一組 base64 encoding(例:都會被 base64 轉譯為 dGl0YW4=)。所以可以說,base64 可用於不同編碼方式之間進行數據轉換後的數據傳輸。

    如果要寬鬆一點說,base64 會有點類似於加密,只不過 base64 可以被輕易的反編碼、讀取到原文,並且此資訊公開給所有人,沒有公私鑰授權特定用戶的功能,所以嚴格來說 base64 不能算是一種加密 (encryption)方法,只能算是一種編譯(encoding)方法。

  2. 便於圖片、音樂、多媒體文件等資訊在網路世界中傳遞

    在 HTTP、SMTP 等網絡通訊協議中,想要傳送圖片等多媒體資訊其實會有很多限制。HTTP、SMTP、POP3、FTP 等網絡協議只能傳送 text form data,但是圖片、音樂、多媒體文件等資訊通常都會使用 binary code 來儲存資訊:比如像素、顏色深度、每幀畫面、音頻等,通常會用 16 或 32 bit 資訊做紀錄。

    所以當我們想要用電子郵件傳送圖片時,我們會需要先將「00101010101000001」的圖片 binary data,用 base64 轉換為「MDAxMDEwMTAxMDEwMDAwMDE=」,電子郵件的 html 形式會類似於 <img> MDAxMDEwMTAxMDEwMDAwMDE= </img>。之後傳送到目的方時,再使用 base64 decode 那串 text,就可以看得到<img> </img> 裡的內容了。

base64 轉換規則

以string input為例,大致上來說 base64 有四個步驟:

  1. 將 string 依照編碼(eg: ASCII) 轉換為 binary。我們知道 ASCII 編碼一共有 256 種可能,所以轉換後每個字母會對應到 0~255 ASCII number,與8 個bits。

  2. 將這些 binary 以每 6 bits 為單位進行分組。

  3. 將每組 bits 轉換為對應的 base64 character:

    • (1) base64 有一個自己的 character set, 只有 64 個 character,分別是:大小寫字母(A-Z、a-z)、數字(0-9)和兩個特殊字符(+ 和 /)。這也是 base64 得到這個名字的原因。

    • (2) 每組 binary 有6 bits, 相當於一共有 64 種可能性,正好可以對應到 base64 character set。

  4. 如果原始的 string長度不是3的倍數,base64 會自動補上一個 “="。

    • (1) 這麼做的理由相當於宣告最後一組bits「後面有補0,才得以是 6個bits」

    • (2) “=” 相當於宣告這組 bits的特殊性

  5. 最後將轉換後的 base64 characters回傳,即大功告成。

值得注意的是,base64 編碼後的數據通常比原始數據更長。這是因為 base64 編碼會將原本以 8 bits 為單位的 string,重新打散單位、轉換為 6 bits 的 base64 characters,這導致編碼後的數據會比原始數據增加約 33% 的大小。

舉例說明

假設我們有一串 string名為「Hello」,其長度=5,不是3的倍數。

  1. 依照 ASCII 編碼,「Hello」每一個 character 會對應到一個 8 bits 組合(=對應到 0~ 255 之間的一個數字)。組合起來即為「01001000 01100101 01101100 01101100 01101111」,一共 40 bits。

  2. 將以上 40 bits 每 6 個分一組,一共可以分成 6 組有完整 6 bits + 1組只有4 bits:「010010」、「000110」、「010101」、「101100」、「011011」、「000110」、「1111」

  3. 最後那一組補 0,讓他還是擁有 6 bits, 只不過後面放入一個 base64 character: “=",標註他的特殊性。所以就變成以下:「010010」、「000110」、「010101」、「101100」、「011011」、「000110」、「111100=

  4. 每一組對應到 base64 character set中的數字(對應表可以參考本篇文章的首頁圖)。

  5. 最後的 output即為 「SGVsbG8=」

python library 實作

在 python中,有很便利的 library協助我們做到 base64 encoding. 可以參考以下的 code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import base64

original_text = "Hello, world!"

original_bytes = original_text.encode('utf-8')

encoded_bytes = base64.b64encode(original_bytes)
encoded_text = encoded_bytes.decode('utf-8')

print("Base64 編碼結果:", encoded_text)

decoded_bytes = base64.b64decode(encoded_bytes)
decoded_text = decoded_bytes.decode('utf-8')

print("Base64 解碼結果:", decoded_text)

結果如下:

1
2
Base64 編碼結果: SGVsbG8sIHdvcmxkIQ==
Base64 解碼結果: Hello, world!
Written By Elaine Hsieh, Taipei Taiwan
Built with Hugo
Theme Stack designed by Jimmy