パスワードハッシュ化で用いるソルト(Salt)とペッパー(Pepper)/シークレットソルト(Secret Salt)の役割と効果

## はじめに
### 背景
Webサービス開発等でパスワードをデータベースに保存する際、ソルトを付与した上で一方向関数等を用いて(ハッシュ化して)保存することはすでに一般的です。
http://www.atmarkit.co.jp/ait/articles/1110/06/news154.html

ソルトだけでなく何らかの秘密文字列を含んだ上でハッシュ化して保存する実装を目にしたことがある方もいると思います。
Rails向けのdeviseGemでも"Pepper"という名前でそういった実装がされています。
https://qiita.com/kenjiszk/items/d38e398bc120cf4bbd1c#%E3%82%BD%E3%83%AB%E3%83%88%E3%82%A2%E3%83%B3%E3%83%89%E3%83%9A%E3%83%83%E3%83%91%E3%83%BC-salt--pepper
また、ハッシュ値自体を暗号化する方もいるかと思います。

### この記事の目的
今回は、主にWebサービスを想定してSaltとPepperの役割の違い等について紹介したいと思います。
多くの開発者にとってPepperという言い回しは聞いたことがなくとも、新しい概念ではなく既知のものでしょう。
しかしながら、あまり一般的な呼び方がなく、言葉で意思疎通が難しいと課題に感じています。
Pepper以外にメジャーな呼び方があるのであれば知りたいです！

### 追記(2018/12/12)
ついに!!他のメジャーな呼び方に気づきました。
SecretSalt(シークレットソルト)です。

NISTの資料にも乗っていました。

> 加えて，Verifierは，自身だけが知っているシークレットをソルト値として用いた鍵導出関数の適用を追加で実施すべきである(SHOULD)．もし可能ならばソルト値はApprove済み乱数生成器 [SP 800-90Ar1]を利用して生成されるべき(SHOULD)であり，SP 800-131Aの最新版で指定されている最小のセキュリティ強度(本書の公開日時点では112ビット)を少なくとも備えるべき(SHOULD)である．このシークレットソルト値は，ハッシュ化された記憶シークレットとは別に(例えば，ハードウェアセキュリティモジュールなどの特別なデバイスの中に)保存されるものとする(SHALL)．この追加の適用により，シークレットソルト値が秘密である限り，記憶シークレットのハッシュに対するブルートフォース攻撃は非現実的である．
https://openid-foundation-japan.github.io/800-63-3-final/sp800-63b.ja.html
https://openid-foundation-japan.github.io/800-63-3-final/sp800-63b.html
NIST Special Publication 800-63B

SHOULDで定義されています。
112bitという具体的な長さの指定等もありますね。

## Salt
### Saltの役割と効果
Saltの役割は下記の二種類です。

- 同じパスワードでも算出されるハッシュ値が異なるようにすること
- 一方向関数への入力文字列を長くすること

これらにより、

- 平文を前提に事前計算された攻撃準備の結果を無効化する
    - (攻撃例)よく使われるパスワードの辞書等を用いてハッシュ値との対応表を作成する等
- 攻撃するための事前計算で作成する逆引き表の作成コストを増大させる
    - (攻撃例)レインボーテーブル等

といった効果が得られます。
上記効果を得るためには、

- 十分な長さを持つこと
- レコード毎に異なること

といった性質が求められます。
http://www.atmarkit.co.jp/ait/articles/1110/06/news154_3.html

### Saltの課題
一般に、「Saltはレコード毎にことなる必要がある」、という性質から、パスワードのハッシュ値が保存されるDB上に保存されます。
あるいは、ハッシュ値が保存されるDB内の情報から推測可能な値になることもあります。
そのため、SQLInjection等でDBのデータが漏洩した場合、オフライン攻撃を行うための十分な情報が攻撃者の手に渡ります。
ストレッチング等をもちいていれば、十分推測困難なパスワードを用いてるユーザへの攻撃は難しくすることは可能ですが、オフライン攻撃が可能な状況となっていることは変わりません。
辞書型攻撃は簡単に成立してしまうでしょう。
下記の徳丸先生の記事でも同様の課題が指摘されており、ハッシュ値を暗号化する手段が提案されています。
http://www.atmarkit.co.jp/ait/articles/1110/06/news154_2.html

## Pepper
いよいよ本題です。ここまでは徳丸先生の記事をほぼ抜粋しているだけですね・・・。

### 一般にPepperという言葉について

Pepperと言う言葉には、Webサービスに限らないセキュリティの文脈では、

- 一方向関数等に入力を与える際に用いる追加の秘密情報

と言った意味合いがありそうです。

パスワードハッシュ化前にPepperを足して、検証事総当りするという提案の論文等で、Pepperという単語が使われています。
Saltとの違いとしては、Saltには攻撃者にとって"秘密"という前提が含まれなさそう。
https://www.usenix.org/legacy/events/sec99/full_papers/kedem/kedem.pdf
https://tools.ietf.org/html/rfc4793#section-6.4

### Webサービス開発文脈におけるPepperとは
Webサービスの文脈でのPepperはどんなものでしょうか。
論文等の明確な定義は見つけられませんでしたが、下記のような使用のされ方が多いようです。

- Saltやハッシュ値とは分離されて保存された、システム固有の秘密情報であって、パスワードをハッシュ化する際にSaltのように付加するもの
- Saltのようにレコード毎に変化させるのではなく、グローバルな値であるもの

例:

- https://blog.ircmaxell.com/2012/04/properly-salting-passwords-case-against.html
- https://blog.filippo.io/salt-and-pepper/

Webサービスにおいては、ソルトの課題で触れたDBの情報だけで総当り攻撃を受けない、と言った役割を担わせるものをPepperと呼ぶことができ、ある程度実際に呼ばれているようです。
何を「秘密情報」とするかは、想定する攻撃によるとは思いますが（ソースコードまで？ファイルシステムまで？)、一般にパスワードのハッシュ値自体よりもセキュリティレベルが高い場所に置かれていれば、その役割を果たせるのではないでしょうか。

### 役割と効果
上記のような文脈・導入方法でのPepperの役割は、

- Saltとハッシュ値以外に秘密情報が付加された状態でパスワードをハッシュ化すること

であり、その効果は、

- DB等の情報が漏洩した状態で、オフライン攻撃を困難にすること
    - （攻撃例）SQLInjection

という効果が得られます。
上記効果を得るためには、

- ハッシュ値・ソルトよりもセキュリティレベルが高い場所に分離して格納する必要がある

ということになるかと思います。
DBのレコード暗号鍵と似た管理方法が必要になるかと思います。
よって、ハッシュ値自体を適切に管理された鍵で暗号化することによってでも、同等の効果が得られると考えられます。

## おわりに
Pepperという呼び方にこだわりはありませんが、何らかこの目的を果たすものに対する呼び方が定まると便利だと思います。
他のレコードでDBのレコードの暗号化をすでに行っている場合は、Pepper的なアプローチよりも、ハッシュ値自体の暗号化をするほうが実装的には軽いのでは？と思いました。
あれ、Pepperとかいらない・・・？
もしかして暗号化のほうが楽・・・？
そういった結論もありうるかもしれません。

## 参考リンク
https://crypto.stackexchange.com/questions/20578/definition-of-pepper-in-hash-functions?answertab=oldest#tab-top