Nano ID 是一個由 21 個字元組成的隨機字串,採用固定的 64 個符號字母表,包含大小寫字母、數字、底線和連字號,預設情況下具備 126 位元的熵。本地產生一個這樣的字串意味著使用密碼學等級的隨機數,這就是為什麼「產生 nanoid」這項請求是建立 URL 友善識別碼時的常見作業。21 個字元的預設值由官方 Nano ID 專案設定;因為 64 個符號的字母表剛好容納於六個二進位位元中,所以每個字元貢獻六個位元,使得預設的空間遠超過 10 的 37 次方。若要批次產生,你可以選擇介於 1 到 128 之間的長度,以及介於 1 到 100 之間的數量,瀏器會從 window.crypto.getRandomValues 抽取位元組,再以拒絕取樣(rejection sampling)將這些位元組對應到字母表上,且不偏袒任何符號。最終結果是一個以換行分隔的字串清單,你可以直接貼到程式碼、資料庫種子檔或設定檔中。所有運算都在當前分頁內完成,因此任何識別碼、數量、長度、剪貼簿內容、隨機位元組或使用事件都不會離開這個頁面。

generate nanoid
generate nanoid

Nano ID 字串的結構解析

Nano ID 規格由 官方存放庫 維護,固定使用 64 個可列印 ASCII 字元的字母表,預設長度為 21。選擇這個組合是因為每個索引剛好容納六個位元,使得字母表能乾淨地對應到隨機位元組範圍,且預設長度具備 126 位元的熵。這個字母表刻意避開斜線、加號、等號、空白字元,以及其他在 URL 路徑或查詢字串中經常需要百分比編碼的字元。Nano ID 不是 UUID,也不聲稱相容於 RFC 4122;它是一種獨特的精簡格式,擁有自己的碰撞數學。它不內嵌時間戳、計數器、區域代碼或記錄類型,因此你無法依建立時間排序,也無法從字元本身推斷出商業意涵。

這個設計會衍生出兩個實際的後果。第一,Nano ID 能輕鬆融入 UUID 顯得笨重的地方:短網址、對外公開的分享權杖,以及已經帶有時間戳前綴的日誌行。第二,因為內嵌的資訊中沒有順序,相隔一毫秒產生的兩個 ID 無法透過字典序比較來判斷哪一個先產生。如果你的應用程式需要離線的時序排序或可解析的時間戳,採用具備時間感知能力的格式(例如 ULID 或 UUID v7)會更合適,因為 Nano ID 從未設計用來承載這類資訊。

如何批次產生 Nano ID

在瀏覽器中開啟 Nano ID 產生器,並依照工具內部強制執行的相同三個步驟操作。

  1. 選擇介於 1 到 128 之間的 ID 長度,以及介於 1 到 100 之間的數量。預設長度 21 與官方 Nano ID API 一致,是最安全的起點,除非你有經過評估的理由才偏離它。
  2. 產生批次。工具會從 window.crypto.getRandomValues 抽取位元組,套用對計算出的位元遮罩進行拒絕取樣,並從官方的 64 個符號字母表中組裝每個字串。
  3. 複製以換行分隔的 ID 並貼到任何合適的位置,然後在你的持久資料儲存層再次強制執行唯一性,而不是單獨信任產生器。

如果批次中包含重複項(統計上不太可能但仍有可能發生),工具會回報錯誤,而不是靜默地回傳那個碰撞的字串。這道防護機制僅限於該批次內部;它不會與其他分頁、裝置、程序、部署或先前的工作階段所產生的 ID 進行協調,這也是為什麼第三個步驟會指向資料庫而非頁面本身。

Web Crypto 與拒絕取樣如何決定輸出結果

Nano ID 的字元分布只會與餵入它的隨機來源一樣可靠。Nano ID 產生器使用 W3C Web Cryptography API 的基礎介面 window.crypto.getRandomValues,這是瀏覽器認可的密碼學強隨機位元組來源。它不會呼叫 Math.random,也不會從時間戳、計數器、瀏覽器指紋或確定性種子衍生出值。當識別碼必須難以預測時,這項區別至關重要,但它仍然無法把識別碼變成身分驗證、授權、加密或密鑰管理系統;存取控制必須獨立於「ID 看起來是否隨機」這件事。

抽取位元組只是問題的一半。另一半是如何在沒有偏差的情況下,將這些位元組對應到 64 個符號的字母表上。天真的做法是對字母表長度取隨機位元組的模數,但只要位元組範圍無法被字母表大小整除,就會引入不均勻的分布。官方 Nano ID 演算法透過拒絕取樣來避開這個問題:它計算出一個剛好覆蓋字母表範圍內所有值的位元遮罩,反覆抽取位元組直到每個索引都落在範圍內,並丟棄其餘的位元組。因為預設的字母表剛好有 64 個符號,且其索引容納於六個位元中,遮罩與整個位元組對齊,實務上拒絕的情況相當罕見。測試會鎖定具代表性的字母表索引,以及所有長度與數量的邊界,以確保實作不會產生偏差。

選擇長度及其成本

長度控制了識別碼隨機空間的大小。在 64 個符號的字母表下,每個字元代表六個位元,因此可用空間會隨著長度增加而呈指數成長。縮短預設長度是以熵換取較短的字串,而成本也是指數級的。下表摘要說明了長度、總位元數與最終空間概略規模之間的關係。

長度熵(位元)概略空間
1060~1.15 × 1018
1696~7.92 × 1028
21(預設)126~8.51 × 1037
32192~6.28 × 1057
64384~3.94 × 10115

這些數字假設符號挑選是均勻的,而拒絕取樣正是讓這個假設成立的關鍵。適合你專案的長度取決於預期的發行量、可接受的碰撞風險、重試行為、儲存生命週期,以及暴露面對抗性有多高。一個永久存在的對外公開分享連結,比一個內部資料列識別碼(帶有唯一性約束與重試迴圈)需要更多熵。這個頁面不會承諾任何選定的長度都能避免碰撞,因為沒有任何有限長度的隨機識別碼做得到這一點。

將產生的 ID 整合到實際應用中

把批次貼到你的程式碼庫中,方式與處理其他不透明識別碼時相同,然後補上產生器本身無法提供的安全防護。最簡單可靠的模式如下所示。

  • 除非有經過評估的需求證明採用其他長度合理,否則請保留 21 個字元的預設值;變短很少會更安全,而短於預設值的 ID 會隨著集合擴大而提高碰撞機率。
  • 把值儲存在一個會保留大小寫與兩種標點符號的欄位中。區分大小寫的定序規則可能會讓兩個僅大小寫不同的 ID 發生靜默碰撞,而會去除連字號或底線的欄位則會破壞字母表的完整性。
  • 在資料庫層級加上唯一性約束。應把這個約束視為唯一性的最終依據,而不是產生器在頁面內部執行的任何檢查。
  • 僅針對失敗的插入進行原子化重試,而不是先檢查再寫入。「先檢查再寫入」的模式會與並行插入產生競爭條件,這正是重複項溜進正式環境的方式。
  • 測試每一個處理 ID 的路由、日誌處理器、分析管線、CSV 匯出功能與資料庫定序規則,因為每一個環節都有可能截斷、改為小寫或去除標點符號。

這些建議與讓 UUID 在正式環境中保持健康狀態的建議相同。Nano ID 雖然簡短,但圍繞它的運維紀律與任何不透明識別碼完全一致;而簡短字串的長度反而讓粗心的日誌或傳輸處理更容易被察覺,因為失敗會在字母表中留下明顯的缺口。

Nano ID 產生器依設計採純前端執行,這讓它非常適合用於位於嚴格資料落地邊界後的程式碼。瀏覽器擴充功能、被入侵的腳本以及遭到入侵的裝置仍可能觀察到頁面資料,因此請勿在此產生密碼、API 金鑰、還原碼、簽章材料或其他高價值秘密。針對這些用途,請使用目標平台專屬的憑證產生器與密鑰管理工具,並把本工具保留給它原本設計要產生的 ID 使用。

延伸閱讀:在本機取得 Excel 欄位名稱並匯出為 CSV。

延伸閱讀:在 Postman 中以 HS256 本機產生 JWT 權杖。