<?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>Cryptography on Tarragon</title><link>https://tarrragon.github.io/blog/tags/cryptography/</link><description>Recent content in Cryptography on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 31 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/cryptography/index.xml" rel="self" type="application/rss+xml"/><item><title>Pepper</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/pepper/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/pepper/</guid><description>&lt;p>Pepper 是密碼雜湊過程裡額外混入的一把金鑰，而它&lt;strong>不存在資料庫裡&lt;/strong>。它與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/salt/" data-link-title="Salt" data-link-desc="說明 salt 如何讓相同的密碼產生不同的雜湊值，以及它擋掉哪一類攻擊">salt&lt;/a> 的分工是兩件不同的事：salt 每筆紀錄一個、與雜湊存在一起、用來讓相同的密碼產生不同的值；pepper 全系統共用、存在資料庫之外、用來讓外洩的資料本身不足以開始猜。兩者並用，而少了 pepper 不影響 salt 的作用。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>它的保護是條件性的，範圍要先講清楚：&lt;strong>只在資料庫與金鑰分開淪陷時成立&lt;/strong>。資料隱碼攻擊取走整張表、備份檔案外流、唯讀副本被存取，這幾種是它擋得住的形態；整台伺服器被拿下或應用程式的執行權限被取得時，金鑰跟著走，這一層歸零。要不要加，看的是威脅模型裡前一類路徑的權重，判讀走 &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、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">7.30 使用者密碼儲存&lt;/a>。它與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/memory-hard-function/" data-link-title="Memory-Hard Function" data-link-desc="要解釋為什麼某個密碼雜湊比另一個抗硬體、或判斷沒見過的構造落在哪裡時，用來定位它向攻擊者收的是哪一種費">memory-hard function&lt;/a> 是兩條獨立的軸：後者讓猜測變貴，這一層讓猜測開始不了。&lt;/p>
&lt;p>它與 work factor 解的是不同的問題。Work factor 讓每一次猜測變貴，攻擊者仍然猜得動；pepper 讓攻擊者連猜都開始不了，前提是他沒有拿到那把金鑰。前者的強度隨硬體變快而下降，後者不會——但後者的成立條件比較窄。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>金鑰的住址決定實際強度，差別在於攻擊者取得執行權限之後還剩下什麼。放在應用程式的設定檔或環境變數時，執行環境一旦被取得金鑰立刻可讀。放進外部金鑰管理服務時金鑰不落在應用程式的磁碟上，但應用程式有權呼叫，取得執行權限的攻擊者一樣拿得到——差別在他必須留在線上一次次呼叫，而那些呼叫留下紀錄、也可以被限速。放進&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/hsm/" data-link-title="HSM（Hardware Security Module）" data-link-desc="判斷金鑰材料需不需要脫離軟體邊界、放進不可讀取明文的專用硬體時的核心術語">硬體安全模組&lt;/a>時金鑰不離開模組，性質同前一項而金鑰本身不可導出。後兩者換到的是偵測與限速的機會，見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/key-management/" data-link-title="Key Management" data-link-desc="說明加密金鑰如何產生、保存、輪替，以及還原時如何依賴金鑰">key management&lt;/a>。&lt;/p>
&lt;p>輪替成本由那一層是不是可逆的決定，而不是由它混在雜湊前面還是後面決定。不可逆的接法（與密碼一起做&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/message-authentication/" data-link-title="Message Authentication" data-link-desc="兩個系統用共享密鑰互相呼叫時，用來判斷驗證值保護到什麼範圍、撤銷粒度落在哪一層">訊息驗證&lt;/a>再餵給金鑰衍生函式、或對衍生結果再做一次訊息驗證）都要等使用者登入才換得掉；可逆的接法（用金鑰把衍生結果加密起來存）可以用舊金鑰解、新金鑰包，全庫離線完成。把位置當成判準會對最常見的那一種做法得出相反的答案。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>導入前要定案的是金鑰的保管與備援，因為&lt;strong>遺失 pepper 的後果是全部密碼都無法驗證&lt;/strong>，唯一的出口是全體使用者重設。這個後果的量級高於密碼儲存的其他任何一項決定，所以備份、存取權限與復原演練要在上線前確認，而不是等第一次輪替時才處理。&lt;/p>
&lt;p>紀錄旁要存一個金鑰版本識別，否則輪替期間新舊並存就沒有依據判斷該用哪一把驗證。&lt;/p>
&lt;p>選可逆的接法時要接受它帶進加密自己的失效形態：用確定性的加密或重複使用同一個初始向量時，「兩個帳號的密碼相同」會變得看得出來，而那正是 salt 要消除的性質。&lt;/p></description><content:encoded><![CDATA[<p>Pepper 是密碼雜湊過程裡額外混入的一把金鑰，而它<strong>不存在資料庫裡</strong>。它與 <a href="/blog/backend/knowledge-cards/salt/" data-link-title="Salt" data-link-desc="說明 salt 如何讓相同的密碼產生不同的雜湊值，以及它擋掉哪一類攻擊">salt</a> 的分工是兩件不同的事：salt 每筆紀錄一個、與雜湊存在一起、用來讓相同的密碼產生不同的值；pepper 全系統共用、存在資料庫之外、用來讓外洩的資料本身不足以開始猜。兩者並用，而少了 pepper 不影響 salt 的作用。</p>
<h2 id="概念位置">概念位置</h2>
<p>它的保護是條件性的，範圍要先講清楚：<strong>只在資料庫與金鑰分開淪陷時成立</strong>。資料隱碼攻擊取走整張表、備份檔案外流、唯讀副本被存取，這幾種是它擋得住的形態；整台伺服器被拿下或應用程式的執行權限被取得時，金鑰跟著走，這一層歸零。要不要加，看的是威脅模型裡前一類路徑的權重，判讀走 <a href="/blog/backend/07-security-data-protection/password-storage-and-work-factor/" data-link-title="7.30 使用者密碼儲存：參數會過期的那一類原語" data-link-desc="決定密碼用哪種雜湊與 work factor、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">7.30 使用者密碼儲存</a>。它與 <a href="/blog/backend/knowledge-cards/memory-hard-function/" data-link-title="Memory-Hard Function" data-link-desc="要解釋為什麼某個密碼雜湊比另一個抗硬體、或判斷沒見過的構造落在哪裡時，用來定位它向攻擊者收的是哪一種費">memory-hard function</a> 是兩條獨立的軸：後者讓猜測變貴，這一層讓猜測開始不了。</p>
<p>它與 work factor 解的是不同的問題。Work factor 讓每一次猜測變貴，攻擊者仍然猜得動；pepper 讓攻擊者連猜都開始不了，前提是他沒有拿到那把金鑰。前者的強度隨硬體變快而下降，後者不會——但後者的成立條件比較窄。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>金鑰的住址決定實際強度，差別在於攻擊者取得執行權限之後還剩下什麼。放在應用程式的設定檔或環境變數時，執行環境一旦被取得金鑰立刻可讀。放進外部金鑰管理服務時金鑰不落在應用程式的磁碟上，但應用程式有權呼叫，取得執行權限的攻擊者一樣拿得到——差別在他必須留在線上一次次呼叫，而那些呼叫留下紀錄、也可以被限速。放進<a href="/blog/backend/knowledge-cards/hsm/" data-link-title="HSM（Hardware Security Module）" data-link-desc="判斷金鑰材料需不需要脫離軟體邊界、放進不可讀取明文的專用硬體時的核心術語">硬體安全模組</a>時金鑰不離開模組，性質同前一項而金鑰本身不可導出。後兩者換到的是偵測與限速的機會，見 <a href="/blog/backend/knowledge-cards/key-management/" data-link-title="Key Management" data-link-desc="說明加密金鑰如何產生、保存、輪替，以及還原時如何依賴金鑰">key management</a>。</p>
<p>輪替成本由那一層是不是可逆的決定，而不是由它混在雜湊前面還是後面決定。不可逆的接法（與密碼一起做<a href="/blog/backend/knowledge-cards/message-authentication/" data-link-title="Message Authentication" data-link-desc="兩個系統用共享密鑰互相呼叫時，用來判斷驗證值保護到什麼範圍、撤銷粒度落在哪一層">訊息驗證</a>再餵給金鑰衍生函式、或對衍生結果再做一次訊息驗證）都要等使用者登入才換得掉；可逆的接法（用金鑰把衍生結果加密起來存）可以用舊金鑰解、新金鑰包，全庫離線完成。把位置當成判準會對最常見的那一種做法得出相反的答案。</p>
<h2 id="設計責任">設計責任</h2>
<p>導入前要定案的是金鑰的保管與備援，因為<strong>遺失 pepper 的後果是全部密碼都無法驗證</strong>，唯一的出口是全體使用者重設。這個後果的量級高於密碼儲存的其他任何一項決定，所以備份、存取權限與復原演練要在上線前確認，而不是等第一次輪替時才處理。</p>
<p>紀錄旁要存一個金鑰版本識別，否則輪替期間新舊並存就沒有依據判斷該用哪一把驗證。</p>
<p>選可逆的接法時要接受它帶進加密自己的失效形態：用確定性的加密或重複使用同一個初始向量時，「兩個帳號的密碼相同」會變得看得出來，而那正是 salt 要消除的性質。</p>
]]></content:encoded></item><item><title>Memory-Hard Function</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/memory-hard-function/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/memory-hard-function/</guid><description>&lt;p>Memory-hard function 是刻意讓每次計算都佔用大量記憶體的函式，用途是壓制攻擊者的平行度。防守方與攻擊者跑的是同一個計算，差別在硬體——攻擊者可以用繪圖處理器或訂製晶片同時算幾萬份，而那個優勢的來源是單次計算需要的資源少、複製一份便宜。記憶體改變的正是這個比例：每個平行實例都要獨佔一塊記憶體，於是平行度被晶片上的記憶體總量封頂，而記憶體在晶片面積上的成本遠高於計算單元。它與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/salt/" data-link-title="Salt" data-link-desc="說明 salt 如何讓相同的密碼產生不同的雜湊值，以及它擋掉哪一類攻擊">salt&lt;/a> 解的是不同的問題——salt 讓相同的密碼產生不同的值，這一類函式讓每一次猜測都貴。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>它是密碼雜湊三種收費位置裡的一種。&lt;strong>時間&lt;/strong>（迭代次數）是最直觀的一項，PBKDF2 只有這一項——把迭代數乘以十，防守方與攻擊者的成本同時乘以十，比例不變，這是它抗硬體能力最弱的結構原因。&lt;strong>記憶體&lt;/strong>改變比例，Argon2 的 &lt;code>m&lt;/code> 與 scrypt 的 &lt;code>N&lt;/code> 買的都是這一項。&lt;strong>記憶體的存取形態&lt;/strong>是第三項：讀取位置由資料本身決定時攻擊者無法預先排程搬運，代價是存取形態會洩漏資訊給觀察得到快取行為的旁通道攻擊者，而 Argon2 分成三個變體正是在這條軸上取值。完整的選型判讀走 &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、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">7.30 使用者密碼儲存&lt;/a>。把金鑰放到資料庫之外是另一條獨立的軸，見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/pepper/" data-link-title="Pepper" data-link-desc="威脅模型含「資料庫被拿走而伺服器沒有」時，用來判斷這一層值不值得加、以及它的輪替成本由什麼決定">pepper&lt;/a>。&lt;/p>
&lt;p>bcrypt 在這個框架裡有明確的位置：它有記憶體項但小且不可調。約 4 KB 這個量級至今仍超過繪圖處理器每個核心可用的快取，這是它抗那一類硬體明顯優於 PBKDF2 的原因；而參數不可調意味著硬體變大之後這個優勢不會跟著長。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;p>選它要付的營運代價是記憶體變成容量約束。每次登入常駐的記憶體乘上尖峰併發數就是要準備的量，而這一項在只算 CPU 的容量規劃裡看不到——機器會在尖峰時因記憶體不足而倒，而不是因為算不完。&lt;/p>
&lt;p>另一個容易漏的是平行度參數。它被編進雜湊字串、驗證時必須用同一個值，所以它不是事後可以調的效能旋鈕；而主流函式庫的預設值多半不是 1，照預設跑的話每次雜湊佔用的核心數要跟著放大。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>判別一個沒見過的構造適不適合用來存密碼，用同一組問題：它對什麼收費、那一項調不調得動、調高之後攻擊者的平行度會不會跟著降。三個問題都有答案的才進入比較；直接使用一般用途的雜湊在第一個問題上就出局，因為它的設計目標正好相反。&lt;/p>
&lt;p>合規環境要注意這個性質與核准清單無關。經過驗證的密碼型金鑰衍生函式目前只有 PBKDF2 一種，而它不是記憶體硬化的——受那一類約束時，抗硬體能力要靠迭代次數推到延遲預算的上限來補，補得有限但那是那一格能做的全部。&lt;/p></description><content:encoded><![CDATA[<p>Memory-hard function 是刻意讓每次計算都佔用大量記憶體的函式，用途是壓制攻擊者的平行度。防守方與攻擊者跑的是同一個計算，差別在硬體——攻擊者可以用繪圖處理器或訂製晶片同時算幾萬份，而那個優勢的來源是單次計算需要的資源少、複製一份便宜。記憶體改變的正是這個比例：每個平行實例都要獨佔一塊記憶體，於是平行度被晶片上的記憶體總量封頂，而記憶體在晶片面積上的成本遠高於計算單元。它與 <a href="/blog/backend/knowledge-cards/salt/" data-link-title="Salt" data-link-desc="說明 salt 如何讓相同的密碼產生不同的雜湊值，以及它擋掉哪一類攻擊">salt</a> 解的是不同的問題——salt 讓相同的密碼產生不同的值，這一類函式讓每一次猜測都貴。</p>
<h2 id="概念位置">概念位置</h2>
<p>它是密碼雜湊三種收費位置裡的一種。<strong>時間</strong>（迭代次數）是最直觀的一項，PBKDF2 只有這一項——把迭代數乘以十，防守方與攻擊者的成本同時乘以十，比例不變，這是它抗硬體能力最弱的結構原因。<strong>記憶體</strong>改變比例，Argon2 的 <code>m</code> 與 scrypt 的 <code>N</code> 買的都是這一項。<strong>記憶體的存取形態</strong>是第三項：讀取位置由資料本身決定時攻擊者無法預先排程搬運，代價是存取形態會洩漏資訊給觀察得到快取行為的旁通道攻擊者，而 Argon2 分成三個變體正是在這條軸上取值。完整的選型判讀走 <a href="/blog/backend/07-security-data-protection/password-storage-and-work-factor/" data-link-title="7.30 使用者密碼儲存：參數會過期的那一類原語" data-link-desc="決定密碼用哪種雜湊與 work factor、或懷疑既有參數已經失效時，用來定參數、判斷合規要求有沒有鎖死選項、排複查節奏與規劃升級路徑">7.30 使用者密碼儲存</a>。把金鑰放到資料庫之外是另一條獨立的軸，見 <a href="/blog/backend/knowledge-cards/pepper/" data-link-title="Pepper" data-link-desc="威脅模型含「資料庫被拿走而伺服器沒有」時，用來判斷這一層值不值得加、以及它的輪替成本由什麼決定">pepper</a>。</p>
<p>bcrypt 在這個框架裡有明確的位置：它有記憶體項但小且不可調。約 4 KB 這個量級至今仍超過繪圖處理器每個核心可用的快取，這是它抗那一類硬體明顯優於 PBKDF2 的原因；而參數不可調意味著硬體變大之後這個優勢不會跟著長。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<p>選它要付的營運代價是記憶體變成容量約束。每次登入常駐的記憶體乘上尖峰併發數就是要準備的量，而這一項在只算 CPU 的容量規劃裡看不到——機器會在尖峰時因記憶體不足而倒，而不是因為算不完。</p>
<p>另一個容易漏的是平行度參數。它被編進雜湊字串、驗證時必須用同一個值，所以它不是事後可以調的效能旋鈕；而主流函式庫的預設值多半不是 1，照預設跑的話每次雜湊佔用的核心數要跟著放大。</p>
<h2 id="設計責任">設計責任</h2>
<p>判別一個沒見過的構造適不適合用來存密碼，用同一組問題：它對什麼收費、那一項調不調得動、調高之後攻擊者的平行度會不會跟著降。三個問題都有答案的才進入比較；直接使用一般用途的雜湊在第一個問題上就出局，因為它的設計目標正好相反。</p>
<p>合規環境要注意這個性質與核准清單無關。經過驗證的密碼型金鑰衍生函式目前只有 PBKDF2 一種，而它不是記憶體硬化的——受那一類約束時，抗硬體能力要靠迭代次數推到延遲預算的上限來補，補得有限但那是那一格能做的全部。</p>
]]></content:encoded></item></channel></rss>