2016/05/07(土)今更ながら CRAM-MD5 SMTP認証[RFC2195,RFC2104,RFC1321]

断片的にはインターネット上にアレコレあるのですが、どうもまとまった内容のものがありません。

電子メール送信の不正利用を減らす目的の一環として、送受信時に「認証」という手続きを踏むのですが、昨今では、この部分にかつての主流であった「POP before SMTP」という方式は昨年あたりから淘汰されつつあり、各種 の「SMTP 認証」という方式になってきています。

電子メールを送受信するだけの殆どの一般ユーザなら、電子メール送受信ソフト(MUA)の設定時以外は全く意識することは無いのですが、今般は電子メール送受信の機能そのものを実装するため、仕組みを理解しないことには話 になりません。

CRAM-MD5 方式による認証手順仕様は、POP3/IMAP4向けに策定された RFC2195 で規定されており、そこで使用される HMAC-MD5 方式による暗号化ハッシュでパスワードを検証する仕組みになっています。
考え方としては、クライアント側で提示した HMAC-MD5 ハッシュ値と、サーバ側でクライアント側と同じ手順で算出した HMAC-MD5 ハッシュ値が一致することで「パスワード一致」と見做すわけです。

ここでの暗号化ハッシュの要(かなめ)は MD5 と呼ばれる方式で、任意長のデータ列から「暗号化理論に基づくハッシュ関数」により16バイト(128bit長)のハッシュ値を作りだすもので、RFC1321 にて規定されています。

CRAM-MD5 によるSMTP認証は、以下のようなシーケンスを取ります。
20160508_1.png
[Client -> Server] AUTH CRAM-MD5
[Server -> Client] 334 PDIwMTYwNTA4MDE1NzMyLjQ2QTRFNENGRjUyMUBteDIuYmFzZWtlcm5lbC5uZS5qcD4=
[Client -> Server] aW5mby5leGFtcGxlLmNvbSAxODBjYmQwNTdjNDQxOTFhYjRkOTU2NWQ3MzA2ZmVmZg==
[Server -> Client] 235 Authentication successful.
ここで、
PDIwMTYwNTA4MDE1NzMyLjQ2QTRFNENGRjUyMUBteDIuYmFzZWtlcm5lbC5uZS5qcD4=

という文字列は、
<20160508015732.46A4E4CFF521@mx2.basekernel.ne.jp>
という文字列を Base64エンコードしたもので、クライアント側でデコードして使用します。
この文字列は 「チャレンジ」と言います。認証毎にランダムな文字列です。

クライアント側では、SMTPサーバを利用するユーザ名と当該ユーザパスワードを暗号化したものを Base64エンコードにて送り返します。これを「レスポンス」と言います。

ここで「ユーザパスワードの暗号化」を行うのですが、この部分が CRAM-MD5 方式の核となるところです。以下に生成手順を示します:

・CRAM-MD5 は「鍵付きハッシング」と呼ばれる暗号化を使用します。
 これは、HMAC と言い、RFC2104 にて規定されています。
 ハッシュ関数にMD5 を使うので、HMAC-MD5 と称します。

・RFC2104 によれば、HMAC の算出は、
 H(K XOR opad, H(K XOR ipad, text))
となっています。
H は、ハッシュ関数を意味し、ここでは MD5 です。他に SHA1 などあります。
K は、ここでは平文で示されたパスワードです。例示として userpassword を示します。
opad は、0x5c を64バイト並べた文字列、
ipad は、0x36 を64バイト並べた文字列、
text は、サーバから送られてきた「Base64エンコードされたチャレンジ文字列」をデコードしたものです。

・Phase0 もし、パスワード文字列が 64バイトを超える場合、その文字列の MD5ハッシュ値を算出し、そのハッシュ値16バイトデータ列を以降の処理でパスワード文字列として扱う。

・Phase1 パスワード文字列 'userpassword' と ipad のバイト単位 XOR 演算
 [K XOR ipad]
 以下のようになります。
 20160508_2.png

・Phase2 パスワード文字列 'userpassword' と opad のバイト単位 XOR 演算
 [K XOR opad]
 以下のようになります。
 20160508_3.png

・Phase3 Phase1 で算出した 64バイトデータ列と、サーバから得たチャレンジ文字列を連結し、16バイトの MD5 ハッシュ値を得る。

・Phase4 Phase2 で算出した 64バイトデータ列と、Phase3 で得た16バイトデータ列を連結し、16バイトのハッシュ値を得る。

・Phase5 Phase4 で得たハッシュ値を16進数文字列化する。
 結果は、
180cbd057c44191ab4d9565d7306feff
 となります。

・Phase6 認証対象のユーザ名と、Phase5 で得た32バイト長文字列を半角スペースで連結し、Base64 でエンコード。これをサーバへレスポンスとして送り返す。
 Base64エンコード後の文字列は
aW5mby5leGFtcGxlLmNvbSAxODBjYmQwNTdjNDQxOTFhYjRkOTU2NWQ3MzA2ZmVmZg==
となります。これは、
info.example.com 180cbd057c44191ab4d9565d7306feff
を、そのまま Base64 エンコードしたものです。

パスワードを解析・解読可能な状態でインターネット上に送らないので、セキュリティ的には安全とされていましたが、昨今ではMD5ハッシュの脆弱性が指摘されているのと、CRAM-MD5 認証そのもので電子メール本文そのものが暗号化されるわけでは無いので、この点での脆弱性を指摘する意見があるようです。

ですが後者に関しては言及のレベルや話題の論点が全く異なる話であり、
「認証と通信そのものを混同してるよね」という印象しか個人的には持てません。