●Dovecotのパスワードジェネレータ
Dovecotは標準でdoveadmというコマンドを持っており、これを使うとdovecotの認証に使用するパスワードを作成できます。
ただ、doveadmはCLIベースとなっており、他のプログラムから使う時にはシェルで呼んだりしてあげないといけません。ローカルで動作するものならまだしも、CGIなどでユーザ入力を受け付けるのは結構怖いものです。
doveadmで作るパスワードはPerl/PHP/Pythonなどの外部プログラムで作成できるのですが、今や不人気言語になってしまったPerlの例は探してもありませんでしたので、その作成方法を紹介します。
●対象とする読者層
- Dovecotを/etc/passwdベースの認証からDBベースに移行を考えている人
- DB上のパスワードをdoveadmを呼ばずに直接プログラム上で作成したい人
- (コードゴルフしていない)Perlのコードが読める人
●環境
以下の環境で説明していきます。
- 25号機@FreeBSD12.0R
- Dovecot 2.3.6以降をインストール済み
●用語
説明の便宜上、本コンテンツでは下記のような用語を使用します。
$はプログラムにした時の変数名です。
- $password / パスワード:
ユーザが入力した平文のパスワードです。
- $salt / salt:
saltです。SHA512にはありません。
- $round / ストレッチ回数:
ブルートフォース耐性強化のためのストレッチ回数です。スキーム毎に決まった方法で指定します。BLF-CRYPTは2の階乗、SHA512-CRYPTは回数を直接指定します。
SHA512とSSHA512にはありません。
- $digest / Digest:
パスワード(SHA512)、パスワードとsalt(SSHA512)、またはパスワードとsaltとストレッチ回数(SHA512-CRYPT/BLF-CRYPT)で得られるハッシュ部分です。
SHA512-CRYPTとBLF-CRYPTでは$で区切られたフィールドの最後の部分です。
パスワード、salt、ストレッチ回数が同じなら常に同じになります。
ここでのDigestは便宜上上記のように呼ぶことにしただけで、正式名ではありません。
- $shadow_password / シャドウパスワード:
暗号化済みパスワード文字列です。
SHA512ではDigestと等価です。
SSHA512はdigestとsaltの連結文字列です。
それ以外のSHA512-CRYPTは$6$で、BLF-CRPYTは$2a$で始まる文字列で、saltとストレッチ回数とdigestを含みます。
※SHA512-CRYPTは5,000回の場合のみ、ストレッチ回数はdigestに表示されません。
Digestもそうですなんが、この文字列は特に正式な名前が無いっぽいので、このサイトでは便宜的にこのような名称で呼ぶことにします。
- $scheme / スキーム:
Dovecotのパスワードスキームを標示するプレフィックスで、{}で括られた文字列です。後ろに.B64があればBase64で符号化、.HEXとあればバイナリを[0-9a-f]の文字列で表現したものになります。
例:{SHA512}、{BLF-CRYPT.B64}
- $dovecot_password / Dovecot password / パスワードストア格納用文字列:
Dovecotの認証用のパスワード文字列です。本コンテンツでは$schemeと$shadow_passwordを連結した文字列を指します。
Dovecotの認証用のパスワードストアに格納すべき文字列です。
- $stored_password / Stored password / パスワードストア文字列:
中身は$dovecot_passwordと同じですが、$dovecot_passwordはユーザからのパスワードを入力して生成したもので、$password_storeはDovecot認証用DBから取り出した先頭にスキームが付いた文字列です。
これ全体がDovecot passwordまたはstored password。
|
最後のパスワードストア格納用文字列ですが、スキームの文字列はここでつけなくてもdovecotの認証時に付いていてれば問題ありません。そのためスキームはパスワードストアに格納せず、パスワードストアにはシャドウパスワードのみを格納し、dovecotの設定上にてmysqlのconcat関数で認証時に動的に付与する方法もあります。
ですが、このコンテンツでは説明の都合上、パスワードストア格納用文字列、dovecot_passwordのいずれもスキームの文字列をつけることにします。
また、検証の際の「# doveadm pw -t '$dovecot_password'」コマンドによるパスワードとハッシュの一致の確認作業はこのスキーム文字列が必須です。
●サンプルコード
●ファイル名の説明
- test_<sheme>[-方式]_<crypt|verify>となっています。
- cryptが生成用で、verifyが検証用です。
- BLF-CRYPTのみ方式が2通りあります。
- SHA512は生成のみあります。SSHA256はおまけです。
- 拡張子は公開の都合上「.txt」になっていますので、適宜リネームしてください。
●利用条件・ご注意
- サンプルコードは事前/事後の連絡なく自由に転載・改造してもらって構いません。
- dec_enc.tgzを展開したものが配下のディレクトリに展開されています。
- サンプルコードに意図的な有害コードは含んではいませんが、正しく動作する保証はありません。
- 各章の本文のコメント部分とサンプルコードのコメントは若干異なる部分があります。
●紹介するPasswordスキーム
下記のスキームに対応したパスワード生成用コード紹介します。おすすめはBLF-CRYPTで、妥協案がSHA512-CRYPTです。
MD5とかSHA1はどこ?ですか...。作者には何を仰っているのかよくわかりません。
- SHA512
- SSHA512
- SHA512-CRYPT
- BLF-CRYPT
●SHA512の特徴とデータ構造
特徴を1行でいうと:平文よりはマシ、レインボーテーブル攻撃にもブルートフォース攻撃にも弱い、実装は簡単、今更採用するメリットはない。
プレインテキストをSHA2ファミリのSHA512でハッシュを取っただけです。今となってはセキュリティの低いスキームです。今時こんなスキーム使う人はいないと思いますが、一応紹介します。
平文よりはマシですがレインボーテーブル攻撃に弱く、攻撃者にとっては平文とそれほど変わりません。またSHA512は計算速度が速く、ブルートフォースにも弱いハッシュです。
SHA512は同じパスワードからは同じDigestが出ます。Digestは64Byteです。SHA512はDigest=シャドウパスワードです。
SHA512のDigestはHexにしない限り文字列はデフォルトでBase64で符号化されており、B64をつけてもつけなくても出力は同じです。SHA512のハッシュ後のデータはただ単なる64バイトのバイナリデータですので、Base64かHEX化しないとスクリーンに表示できないためと思われます。。
|
●SSHA512の特徴とデータ構造
特徴を簡単でいうと:SHA512よりはマシ、ブルートフォース攻撃には相変わらず弱い、実装は簡単、今更採用するメリットがない。
レインボーテーブル攻撃に弱いならsaltつければいいじゃない?ということで、SHA512する際に、sha512(plaintext)にするところを、4バイトのsaltをつけて。sha512(plaintext + salt) + saltとして、レインボーテーブル攻撃を強化したのがSSHA512です。
レインボーテーブル攻撃には強いのですが、salt自体は丸見えですので、ブルートフォースには弱いスキームです。今時こんなスキーム使う人はいないと思いますが、一応紹介します。
シャドウパスワードは64ByteのDigestに4文字のsaltが末尾にくっつくので、ハッシュ後のバイト数は68Byteのバイナリ文字列になります。
saltとパスワードが同じであれば、シャドウパスワードは必ず同じ文字列になります。ただ、通常saltはランダム文字列にしてパスワードを作りますので、一般的にはパスワード作るたびに異なったシャドウパスワード(=digest)が出ます。。
SHA512同様に、Hexにしない限り文字列はデフォルトでBase64で符号化されており、B64をつけてもつけなくても出力は同じです。
# doveadm pw -s SSHA512 -p y17 |
私は自鯖でSSHA512用にパスワード生成/確認用スクリプトを実装したのですが、使う理由が全然無いので、salt漬けしており全く使っておりません。
【参考】
SSHA512/SSHA512.B64の必ず末尾は=になります。
SSHA512のBase64化前のシャドウパスワード部分は68Byte=544bitの固定長です。
Base64は6bit x 4の24bitを1セットでエンコードします。544bitをエンコードするためには544÷24=22.6を切り上げ、24bitが23セット分、24x23=552bit必要です。
しかしこれだと552-544=8bit、つまり最後のセットが8bit(2bit+6bit)分必要数に足りません。6bitに満たない部分である2bit分は00がPaddingされますが、エンコードネタが無い=何も文字がない部分の6bitは=でPaddingされます。
●SHA512-CRYPT
特徴を1行でいうと:適切なストレッチ回数を設定すれば各種攻撃耐性が高い、実装は面倒臭い、専用のPerlモジュールがないためcrypt関数に依存。
SSHA512はレインボーテーブルにはある程度強いもののブルートフォースに弱いです。そのブルートフォースへの対策の方法の1つのアプローチが「1回の認証にかかる時間を長くする」です。
SHA512-CRYPTはそんな思想を元にできているようで、「ハッシュ後の文字列とsalt使って何回もハッシュしまくれば時間かかるんじゃね?」という考え方です。
このスキームは割とビンテージOSでも動いたりするので、古いOSやライブラリの充実していないOS上でも使える可能性が高く、ストレッチ回数を細かく設定できるため、低性能なビンテージ機材でも使いやすいスキームです。
ただ、実装は割と面倒くさいのでBLF-CRYPTがまともに運用できるならあまり使う必要のないスキームです。
round回数はデフォルト5,000で1,000〜999,999,999回です。doveadmはデフォルト5,000回で-rオプションで変更できます。
saltは16Byteで、doveadmは[a-zA-Z0-9./]しかsaltに使っていないみたいですが、シャドウパスワードをまるっとB64かHEXでエンコードしてしまえば、実はバイナリ文字列16Byteのsaltでも大丈夫です。
ただ、doveadmはsaltは毎回ランダムではあるものの、引数などで指定できませんので、バイナリ文字列のsaltはdoveadmでは生成できません。doveadmで生成できないというだけですのでシャドウパスワード全体がBase64化済み(またはHEX文字列)であれば検証は問題なく可能です。
シャドウパスワードは$6$で始まり、ストレッチ(round)回数が5,000回であれば「$6$<16Byteのsalt>$digest」、ストレッチ(round)回数が5,000回以外なら「$6$round=<ストレッチ回数回数>$<16Byteのsalt>$digest」となります。
# doveadm pw -s SHA512-CRYPT -p y17
※ スキーム$6$ストレッチ回数$salt$Digest ※ストレッチ回数を明示的に指定すると$6$の後はストレッチ回数ですとなり、そのあとにsaltが続きます。 |
●BLF-CRYPT (a.k.a BCRYPT/bcrypt/Blowfish Crypt)
特徴を簡単にいうと:適切にストレッチすれば各種攻撃体制が高い、ストレッチ処理が重い、実装はSHA512-CRYPTより楽、専用のPerlモジュールが用意されている。
1回の認証にかかる時間を長くしたいんなら、ストレッチ回数をバカの一つ覚えみたいに増やすよりも「そもそも1回のハッシュ計算をクソ重くしてから、何回もハッシュしまくれば効果的じゃね?」という考え方に基づくのがBLF-CRYPTです。
まともに動く環境を用意できるなら、Dovecotのスキームの連中の中では割と実装しやすく、セキュリティの高いオススメのスキームです。
バカの一つ覚えとかSHA512-CRYPTをdisってるように見えますが、SHA512-CRYPTの2倍以上長い回数をストッチングできます。正確にはBLF-CRYPTはSHA2ファミリと異なり、ハッシュ関数じゃなくてどうも暗号化関数らしいのですが、知っててもここでは特に意味がないので無視します。
このスキームは割と新しめで計算が重く、また、ストレッチ回数が2の累乗でしか設定できないため、ビンテージOSやビンテージ機材には辛いかもしれません。また、Perl内蔵のcrypt関数でも作れるのですがこのcryptがbcryptを利用できない状態でPerlパッケージがインストールできない環境では利用が難しいスキームでもあります。
※cryptがbcryptを利用できるかはライブラリ依存です。
ストレッチ回数はデフォルトでdoveadmでは5(2^5=32回)で、設定可能範囲は4(16回)〜31(2,147,483,648回)です。SHA512-CRYPT同様に-rオプションで任意の2の階乗を指定できます。
当然ですがストレッチ回数の指定数を1増やすと処理時間が2倍に増えます。
saltは16Byteでdoveadmでは生成のたびにランダムです。出力結果はsalt、digestともに、それぞれBase64(的な何か)でエンコードするため、バイナリ文字列を使用しても問題ありません。ただdoveadmではsaltには[a-zA-Z0-9./]しか使っていないみたいです。
シャドウパスワードは$2a$で始まり、「$2a$<ストレッチ回数(べき乗表示)2桁>$<22文字がsalt><残った部分がDigest>」となります。SHA512-CRYPTと異なり、ストレッチ回数は必ずシャドウパスワードに出るためストレッチ回数による構造差分はありません。
BLF-CRYPTのストレッチ回数の実用的な値は、運用機材にもよりますが2019年現在では(2^)09〜12回程度だと思います。これより大きくするとメールチェックが体感的に遅くなったと感じるかもしれません。
私は機材が2013年製のローエンド機と古くてしょぼいため(2^)11にしています
# doveadm pw -s BLF-CRYPT -p y17
|
●パスワードの検証について
●コンソール上での確認
コンソール上からはdoveadm pw -t '$dovecot_password'を入力して、正しいパスワードを入れればverifiedと出ます。スキームの文字列は必須です。また$dovecot_passwordをシングルクォートで囲わないと弾かれます。
|
●プログラムorスクリプトからの確認
どのスキームもハッシュ関数で作成していますので、(a).saltや(b).ストレッチ回数はシャドウパスワードから抽出できますが、Passwordはシャドウパスワードからの復号はできません。しかし、スキームとハッシュ回数、ストレッチ回数、Passwordが同じであればシャドウパスワードは必ず同じものが生成されます。
そこでプログラム上は検証したいシャドウパスワードから(a).saltと(b).ストレッチ回数を抽出し、それらとユーザが入力したPasswordでもう一度同じスキームを使用してシャドウパスワードを生成します。
この「もう一度作成したシャドウパスワード」と、「検証したいシャドウパスワード」が一致すれば、入力したパスワードが間違いない、または問題なくコードが書けていることがわかります。
●次のセクションでやること。
- SHA512の実装例
- SSHA512の実装例
●参考文献
- Authentication/PasswordSchemes (from dovecot.org 2019.06.06 accessed)