iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
Software Development

軟體開發養成計畫:以小程式實作深化開發能力系列 第 26

[Day26]Hash ≠ 加密,Base62 ≠ 魔法:資料處理入門基礎

  • 分享至 

  • xImage
  •  

開場

在軟體開發的世界裡,常常會聽到**「雜湊」、「編碼」**這些名詞。剛開始學習時,我也曾把它們和「加密」搞混在一起,結果越查越糊塗。
今天的文章,我會用最簡單的方式帶大家快速入門 Hash 與 Base62 編碼,並聊聊它們在實際應用中,為什麼會這麼重要。

1.雜湊的基礎概念

(1)什麼是雜湊?

雜湊(Hash),或稱為散列,是一種將任意大小的輸入資料轉換成一個固定大小的Hash Value雜湊值或Hash Code雜湊碼的過程,簡單來說就是把任意長度的資料轉換成固定長度的「指紋」。例如一個檔案、一串文字、或任何數據皆可以執行。
這個轉換過程是由一個特殊的演算法完成的,這個演算法就稱為雜湊函式(Hash Function)
轉換流程大概會長這樣:
1.輸入 (Input/Key):任意長度的資料。
2.雜湊函式 (Hash Function):執行轉換的演算法(如 MD5, SHA-256)。
3.輸出 (Output/Hash Value):固定長度的字串或數字。

而且不論我們輸入一個單詞或一本小說,使用同一個雜湊函式,得到的雜湊值長度都會是固定的(例如 256 位元)。

(2)特性:雜湊函式的關鍵性質

一個設計良好的雜湊函式通常具備很多特性,今天我們先來講其中最重要的三種特性:
1.Determinism 確定性
概念:相同的輸入,永遠產生相同的雜湊值。
如果每次雜湊同一個密碼或檔案都得到不同的結果,我們就無法進行比對或驗證。
2.Pre-image Resistance 不可逆性
概念:雜湊過程是單向的,從最終的雜湊值,幾乎不可能反推出原始輸入資料。
系統可以比對我們輸入密碼的雜湊值,而不用擔心儲存的資料(雜湊值)洩露後會暴露原始密碼,這是密碼儲存的關鍵安全保障。
3.Collision Resistance 抗碰撞性
概念:找到兩個不同的輸入資料,但它們卻產生相同雜湊值的情況,是計算上幾乎不可能的事。
如果某人偷偷修改了一個檔案(不同的輸入),但卻能輕易找到一個與原檔案雜湊值相同的雜湊值,那麼雜湊就失去了驗證資料沒有被竄改的能力。

(3)常見應用

雜湊的應用非常廣泛,在資訊科學的世界中到處都能看到它的身影,像是:

  • 數據結構
    在雜湊表的應用中,利用雜湊函式快速計算出資料在記憶體中的儲存位置,實現極快的資料查找、插入和刪除操作,其效率通常優於數組或鍊表。
  • 資訊安全
    系統不會直接儲存使用者的原始密碼,而是儲存密碼的雜湊值。當使用者登入時,系統對輸入的密碼進行雜湊運算,並將結果與儲存的雜湊值比對,確保即使資料庫被盜,駭客也無法輕易得知原始密碼。
    在數位簽章與區塊鏈上也是如此,雜湊被用於生成資料的「指紋」,保證交易或文件的完整性與真實性。
  • 資料完整性校驗
    當我們下載一個大檔案時,網站通常會提供一個該檔案的雜湊值(例如 MD5 或 SHA256)。下載完成後,我們計算本地檔案的雜湊值,如果兩個值完全相同,則證明檔案在傳輸過程中沒有被修改或損壞。

(4)程式碼範例

我們使用 Python 內建的 hashlib 函式庫,示範最常用的 **SHA-256 **雜湊演算法。

import hashlib

def calculate_sha256_hash(data):
    """
    計算輸入資料的 SHA-256 雜湊值
    """
    sha256 = hashlib.sha256()
    sha256.update(data.encode('utf-8'))
    return sha256.hexdigest()

# 範例輸入
text_a = "Hello, Hash World!"
text_b = "Hello, Hash World!"
text_c = "Hello, hash World!" # 改變一個字母的大小寫

# 執行雜湊計算
hash_a = calculate_sha256_hash(text_a)
hash_b = calculate_sha256_hash(text_b)
hash_c = calculate_sha256_hash(text_c)

print(f"輸入 A: '{text_a}'")
print(f"雜湊 A: {hash_a}")
print("-" * 20)

print(f"輸入 B: '{text_b}'")
print(f"雜湊 B: {hash_b}")
print("結果:A 和 B 的雜湊值完全相同(確定性)")
print("-" * 20)

print(f"輸入 C: '{text_c}'")
print(f"雜湊 C: {hash_c}")
print("結果:僅改變一個字母,雜湊值卻完全不同(雪崩效應)")
print("-" * 20)

# 雜湊值長度(SHA-256 固定為 64 個 16 進位字元)
print(f"雜湊值長度: {len(hash_a)} 個字元")

執行結果:
https://ithelp.ithome.com.tw/upload/images/20251002/20178527wE41vY8g9S.jpg
從結果中我們可以看到:

  • 確定性: 只要輸入一樣(A 與 B),輸出就完全一樣。
  • 雪崩效應: 即使輸入只差一個字母的大小寫(A 與 C),輸出的雜湊值也完全不同。(補充:只要輸入資料有微小的改變,輸出的雜湊值也會產生巨大的、難以預測的變化。)
  • 固定長度: 輸出雜湊值(64 個字元)的長度是固定的,與輸入的長度無關。

2.Base62 編碼的基礎概念

(1)甚麼是 Base62 編碼?

Base62 編碼是一種將數字或二進制資料轉換為由 62 個特定字元組成的字串表示形式的編碼方式,名稱由來是因為Base62 由 62 個字元所組成:

  • 數字:0-9 (共 10 個)
  • 大寫字母:A-Z (共 26 個)
  • 小寫字母:a-z (共 26 個)
    10 + 26 + 26 = 62(個)

(2)編碼 (Encoding) ≠ 加密(Encryption)?

Base62 屬於編碼,而不是加密,以下是兩者的比較表格:

比較面向 編碼 (Encoding) 加密 (Encryption)
目的 確保資料能在不同系統中正確儲存與傳輸,例如 Base64、URL Encoding 保護資料機密,確保只有授權者能解讀,例如 AES、RSA
安全性 無安全性,只要知道規則就能還原 高安全性,必須持有密鑰才能解密
特性 可逆、效率高,重點在「資料正確傳遞」 可逆(需密鑰)、計算複雜,重點在「資料保密性」

提醒:Base62 只是將一個大數字或資料用更緊湊、URL-安全的字元集重新表示,它不具備任何保密或安全功能

(3)為什麼要使用 Base62?

Base62 的主要優勢在於其**「URL 友好」和「緊湊性」**:

URL 友好

Base62 的 62 個字元都屬於標準的字母和數字,不包含像 Base64 常用的 +、/ 或補位符號 = 等特殊字元。這些特殊字元在 URL 傳輸中容易引起歧義或需要額外的 URL 編碼,Base62 則能完美避免此問題。

短連結

這是 Base62 最常見的應用。一個龐大的資料庫 ID(例如 64 位元的整數)透過 Base62 編碼後,可以轉換成一個更短且人類可讀的字串,從而大幅縮短網址長度。

節省空間/高效

Base62 雖然沒有 Base64 轉換效率高,但在將純數字 ID 轉換為字串時,能比十進位或十六進位佔用更少的字元空間

(4)Base62 編碼流程

Base62 的編碼原理與我們將十進位數字轉換為二進位、八進位或十六進位的原理完全相同,只不過這裡的基數是 62
1.定義字元集
確定 62 個字元的順序,例如:"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"。
2.將輸入資料轉換為Integer
如果輸入是文字或二進制資料,需要先轉換成一個可以被處理的大整數。
3.除法取餘
將該大整數不斷地除以 62 (基數),每次除法的餘數 (Remainder) 對應到字元集中的一個字元,最後將這些字元逆序組合起來,就是 Base62 的結果。
4.解碼流程
解碼是反向操作,將 Base62 字串視為一個 62 進位數。從左到右遍歷字串,將當前字元在字元集中的索引值,根據位置決定來乘以 62 的次方,然後累加得到原始的十進位數。

(5)程式碼範例

以下使用 Python 實作將一個整數 ID 進行 Base62 編碼與解碼:

import string

BASE62_ALPHABET = string.digits + string.ascii_lowercase + string.ascii_uppercase
BASE = len(BASE62_ALPHABET) # 基數為 62

def encode(num: int) -> str:
    """
    將一個十進位整數編碼為 Base62 字串 (十進位轉六十二進位)
    """
    if num == 0:
        return BASE62_ALPHABET[0] # 處理 0 的特殊情況

    encoded = []
    while num > 0:
        # num 除以 62,得到商 (num) 和餘數 (rem)
        num, rem = divmod(num, BASE)
        encoded.append(BASE62_ALPHABET[rem])

    # 餘數是反向取出的,所以需要反轉字串
    return "".join(reversed(encoded))

def decode(encoded_str: str) -> int:
    """
    將 Base62 字串解碼回十進位整數 (六十二進位轉十進位)
    """
    num = 0
    # 建立字元到其索引值的對應 (用於加速查找)
    char_map = {char: i for i, char in enumerate(BASE62_ALPHABET)}
    
    # 從左到右遍歷 Base62 字串
    for char in encoded_str:
        # 原理: num = num * base + char_value
        # 每次進位都乘以 62
        char_value = char_map[char]
        num = num * BASE + char_value
        
    return num

original_id = 987654321012345 # 數據庫 ID
print(f"原始 ID: {original_id}")

# 1. 執行編碼
short_code = encode(original_id)
print(f"Base62 編碼結果: {short_code}")
print(f"長度從 {len(str(original_id))} 縮短為 {len(short_code)}")

# 2. 執行解碼
decoded_id = decode(short_code)
print(f"Base62 解碼結果: {decoded_id}")

# 3. 驗證結果
print(f"驗證是否還原成功: {original_id == decoded_id}")

執行結果:
https://ithelp.ithome.com.tw/upload/images/20251002/201785278lb3Sg9laU.jpg

3.Hash 與 Base62 的結合應用 — 短網址系統案例

在短網址服務中,最核心的問題就是:如何把一個超長的 URL,轉換成短小且唯一的代碼?
這時候,我們可以結合 Hash 與 Base62 來達到目標,過程大致如下:
1.Hash 處理
先對長網址做 Hash,確保每個網址能對應出一個相對「唯一」的數值,例如使用 MD5、SHA-256 等演算法。
2.取部分數值
完整的 Hash 太長,我們只需要取一部分,避免過度冗長。
3.Base62 編碼
將數值再透過 Base62 轉換成短字串,這樣可以有效壓縮字串長度,並確保生成的短碼適合放進網址中。
4.資料庫映射
把這個短碼存進資料庫,對應到原始網址。使用者透過短網址存取時,就能快速找到對應的長網址並跳轉。

這其實就是第 25 天短網址小程式背後的原理延伸,只是多了 Hash → Base62 → 儲存/查詢 這一整套流程。


上一篇
[ Day25 ] 小小短碼,大大學習:用 Python 打造屬於自己的短網址工具
下一篇
[Day27]Python 圖片壓縮技巧:PictureCompressor
系列文
軟體開發養成計畫:以小程式實作深化開發能力30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言