<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Salt on Tarragon</title><link>https://tarrragon.github.io/blog/tags/salt/</link><description>Recent content in Salt on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 29 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/salt/index.xml" rel="self" type="application/rss+xml"/><item><title>Salt</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/salt/</link><pubDate>Wed, 29 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/salt/</guid><description>&lt;p>Salt 的核心概念是「在雜湊之前混入一段每筆紀錄各不相同的隨機值」。它讓兩個使用者即使設了相同的密碼，存下來的雜湊值也不一樣，因此它保護的是儲存於資料庫的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/credential/" data-link-title="Credential" data-link-desc="整理身分驗證與系統存取用秘密資料">credential&lt;/a> 在整份外洩之後的階段。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Salt 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/at-rest-encryption/" data-link-title="At-Rest Encryption" data-link-desc="說明資料落到儲存媒介前的加密層，以及它對應的威脅模型">at-rest encryption&lt;/a> 服務的階段不同——後者讓資料在被取得時不可讀，salt 假設資料已經可讀、處理的是逐一破解的成本結構。它與 work factor 則是兩種互補的保護：work factor 決定猜一次要付多少代價，salt 決定攻擊者能不能一次猜完所有人。它不需要保密——salt 與雜湊值存在一起是標準做法，它的作用來自唯一性而非機密性。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>沒有 salt 時，同一個密碼在整份資料裡永遠對應同一個雜湊值，於是兩件事同時成立：攻擊者可以拿預先算好的對照表（rainbow table）直接反查，也可以掃出「哪些帳號用了同一個密碼」再從一個突破口推到其他帳號。加了 salt 之後這兩條路徑都要對每個帳號各算一次，攻擊成本因此隨帳號數量線性成長。&lt;/p>
&lt;p>判斷一套系統有沒有 salt，看的是同一個密碼在不同帳號上存下來的值一不一樣。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>salt 由誰負責取決於用的是哪一層 API，而不是取決於演算法。&lt;strong>高階的密碼雜湊 API&lt;/strong>（輸入密碼、輸出一串自描述字串）自己產生與管理 salt，並把它連同演算法識別與參數一起編碼進輸出，驗證時再讀回來——設計責任因此只剩下選對 API。&lt;strong>低階的金鑰衍生 API&lt;/strong>（輸入密碼加 salt、輸出裸位元組）不管這件事：salt 要自己產生、儲存格式要自己設計，而參數也不會被寫進輸出，那些系統因此容易把參數寫死在程式碼裡。同一個演算法在同一個語言裡兩種形態都存在，Argon2 與 scrypt 尤其常見。&lt;/p>
&lt;p>判別方法看回傳值：以 &lt;code>$&lt;/code> 開頭的字串代表 salt 與參數已經被管理，回傳位元組陣列代表兩者都是呼叫端的責任。而自己拼接 salt 與密碼再送進一般用途的雜湊，是這一格最常見的實作錯誤——它兩層都不屬於。&lt;/p>
&lt;p>Salt 保護不了弱密碼本身——它讓攻擊者無法一次攻擊全部帳號，單一帳號的破解成本仍然由 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/password-storage-and-work-factor/" data-link-title="7.30 使用者密碼儲存：參數會過期的那一類原語" data-link-desc="決定密碼用哪種雜湊與 work factor、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">work factor 與演算法選型&lt;/a> 決定。不可逆單向轉換在原語分類中的位置見 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/cryptographic-primitive-selection/" data-link-title="7.28 密碼學原語選型：金鑰位置決定威脅模型" data-link-desc="決定用加密、簽章、編碼還是單向轉換保護一段資料時，用來判斷各原語的保護範圍與失效條件">7.28 密碼學原語選型&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Salt 的核心概念是「在雜湊之前混入一段每筆紀錄各不相同的隨機值」。它讓兩個使用者即使設了相同的密碼，存下來的雜湊值也不一樣，因此它保護的是儲存於資料庫的 <a href="/blog/backend/knowledge-cards/credential/" data-link-title="Credential" data-link-desc="整理身分驗證與系統存取用秘密資料">credential</a> 在整份外洩之後的階段。</p>
<h2 id="概念位置">概念位置</h2>
<p>Salt 與 <a href="/blog/backend/knowledge-cards/at-rest-encryption/" data-link-title="At-Rest Encryption" data-link-desc="說明資料落到儲存媒介前的加密層，以及它對應的威脅模型">at-rest encryption</a> 服務的階段不同——後者讓資料在被取得時不可讀，salt 假設資料已經可讀、處理的是逐一破解的成本結構。它與 work factor 則是兩種互補的保護：work factor 決定猜一次要付多少代價，salt 決定攻擊者能不能一次猜完所有人。它不需要保密——salt 與雜湊值存在一起是標準做法，它的作用來自唯一性而非機密性。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>沒有 salt 時，同一個密碼在整份資料裡永遠對應同一個雜湊值，於是兩件事同時成立：攻擊者可以拿預先算好的對照表（rainbow table）直接反查，也可以掃出「哪些帳號用了同一個密碼」再從一個突破口推到其他帳號。加了 salt 之後這兩條路徑都要對每個帳號各算一次，攻擊成本因此隨帳號數量線性成長。</p>
<p>判斷一套系統有沒有 salt，看的是同一個密碼在不同帳號上存下來的值一不一樣。</p>
<h2 id="設計責任">設計責任</h2>
<p>salt 由誰負責取決於用的是哪一層 API，而不是取決於演算法。<strong>高階的密碼雜湊 API</strong>（輸入密碼、輸出一串自描述字串）自己產生與管理 salt，並把它連同演算法識別與參數一起編碼進輸出，驗證時再讀回來——設計責任因此只剩下選對 API。<strong>低階的金鑰衍生 API</strong>（輸入密碼加 salt、輸出裸位元組）不管這件事：salt 要自己產生、儲存格式要自己設計，而參數也不會被寫進輸出，那些系統因此容易把參數寫死在程式碼裡。同一個演算法在同一個語言裡兩種形態都存在，Argon2 與 scrypt 尤其常見。</p>
<p>判別方法看回傳值：以 <code>$</code> 開頭的字串代表 salt 與參數已經被管理，回傳位元組陣列代表兩者都是呼叫端的責任。而自己拼接 salt 與密碼再送進一般用途的雜湊，是這一格最常見的實作錯誤——它兩層都不屬於。</p>
<p>Salt 保護不了弱密碼本身——它讓攻擊者無法一次攻擊全部帳號，單一帳號的破解成本仍然由 <a href="/blog/backend/07-security-data-protection/password-storage-and-work-factor/" data-link-title="7.30 使用者密碼儲存：參數會過期的那一類原語" data-link-desc="決定密碼用哪種雜湊與 work factor、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">work factor 與演算法選型</a> 決定。不可逆單向轉換在原語分類中的位置見 <a href="/blog/backend/07-security-data-protection/cryptographic-primitive-selection/" data-link-title="7.28 密碼學原語選型：金鑰位置決定威脅模型" data-link-desc="決定用加密、簽章、編碼還是單向轉換保護一段資料時，用來判斷各原語的保護範圍與失效條件">7.28 密碼學原語選型</a>。</p>
]]></content:encoded></item></channel></rss>