「ぷららメールサーバーがセキュリティ対策を実施」でSMTPの認証方式「CRAM-MD5」が廃止された。暫定対策のため「 Panasonic ドアフォン VL-SWH705KLのメール通知が停止した-調査結果」でDebian10にPostfixメールサーバーを稼働させCRAM-MD5認証でメールを受け、ぷららメールサーバーへPLAIN認証を使いTLS接続で中継させた。
平文パスワードを送信する必要がある「LOGIN」と「PLAIN」認証だけにするとは、どの様な「セキュリティ」対策なのだろうか?
「LOGIN」「PLAIN」認証より「CRAM-MD5」認証の方がよほどセキュリティが高い気がする。
「DIGEST-MD5」認証とか「OAUTH」認証とかを加えるなら理解出来るが、、、
今更だが、Postfixメールサーバーで「CRAM-MD5」と「DIGEST-MD5」の認証をsmtpコマンドレベルで確認してみた。
(1)CRAM-MD5
「CRAM-MD5」は、検索するとコマンドレベルでの確認事例が出てくる。
smtp接続後、コマンドで「auth cram-md5」で認証方法を提示すると「base64でエンコードされたチャレンジ文字列」応答がある。
チャレンジ文字列をアカウントのパスワードをkeyとしてHMAC-MD5でhashコードを生成する。
ユーザIDとhashコードをスペース連結した文字列をbase64エンコードしてチャレンジ応答とする。
「CRAM-MD5でSMTP認証テスト」を参考にして掲載のシェルスクリプトをmacOS Big Sur(11.7.10)で実行したがエラーとなる。赤字の修正で正しいレスポンスコードが生成される。
Debian10には、「/usr/bin/gen-auth」がインストールされていた。
(2)DIGEST-MD5
DIGEST-MD5(RFC2831)を検索してもコマンドレベルでの確認事例を見つけられなかった。「今更ながら DIGEST-MD5 SMTP認証」が詳細にプロトコルを記載していた。これを参考にDIGEST-MD5を確認してみた(「今更ながら CRAM-MD5 SMTP認証」も参考になった)。
「今更ながらDIGEST-MD5 SMTP認証」には、パラメータ類とhash計算結果が書かれているのでmacOSのopensslを使い計算式をシュミレートしてみた。同記事 「2)レスポンスMD5ハッシュの生成」で式の H( { username-value, ":", realm-value, ":", passwd } を計算するとHEX表記で記載値と一致する。この値を使い、HEX(H(A1))値を計算する値が違う。。。
示されたハッシュ値は「46 a5 d6 cc c1 56 d8 ca 8d a9 70 72 3d 45 5d 17」
「H( { username-value, ":", realm-value, ":", passwd } )」を「HEX( H( { username-value, ":", realm-value, ":", passwd } ) )」としてハッシュ値を確認もしてみた。やはり違うようだ。
同記事「2-2) A2 データ列の生成」の式
A2 = { "AUTHENTICATE:", digest-uri-value }はで一致する。
smtpコマンドデータを検証するため、XPS8300上のDebian10にpostfixを稼働させ、VAIO上のDebian10を使い、DIGEST-MD5認証でメール送信する設定をしたEvolutionメールツールで送信を行い、Wiresharkでsmtpコマンドをキャプチャして確認した。
「auth digest-md5」smtpコマンドに対しサーバーから「334 ---challenge」の応答がある。
base64デコードしてnonce値、realm値を得る。
その他のパラメータは「qop=auth,charset=utf-8,algorithm=md5-sess」。charsetの値は、「"」無し。
responseデータからcnonce値、digest-uri値を得て、opensslで確認すると、式H( { username-value, ":", realm-value, ":", passwd }は、md5バイナリー値にnonce値とcnonce値を":"で結合したデータがA1となる事が判った。
記事のA1ハッシュ値は、
「46 a5 d6 cc c1 56 d8 ca 8d a9 70 72 3d 45 5d 17」
でなく
「7a b3 59 07 e6 9a 04 0a d6 38 cb 3c d6 1b a9 ff」
となる。従って、「2-3) response ハッシュ値の算出」のハッシュ値は、
「9a ac 0f 21 5f 22 9c f8 20 83 b8 64 72 a2 78 8d」
となる。
「KD は2つの文字列を':'で連結の意」と注釈されているが、「RFC2617の3.2.1 The WWW-Authenticate Response Header」の解説で結合後にハッシュ化した値とされている。
「2-4) サーバに返すレスポンスデータ列の生成(base64)」は、
「dXNlcm5hbWU9ImluZm8uZXhhbXBsZS5jb20iLHJlYWxtPSJleGFtcGxlLmNvbSIsbm9uY2U9IkpRTUt0ZGdiRWhNcmE0R2RBWW1BalE9PSIsY25vbmNlPSJsV0Z7W1F1aVJqfV9MW1BXIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL214LmV4YW1wbGUuY29tIixyZXNwb25zZT05YWFjMGYyMTVmMjI5Y2Y4MjA4M2I4NjQ3MmEyNzg4ZCxjaGFyc2V0PXV0Zi04」
となる。
base64デコード値は、
「username="info.example.com",realm="example.com",nonce="JQMKtdgbEhMra4GdAYmAjQ==",cnonce="lWF{[QuiRj}_L[PW",nc=00000001,qop=auth,digest-uri="smtp/mx.example.com",response=9aac0f215f229cf82083b86472a2788d,charset=utf-8」
(*charset=の値は、「"」でクォートしていない)
記事の「3)認証確認」で「クライアント側は、肯定応答をするために同じように A2 データ列を上記のものに変更したレスポンスハッシュ値を算出し、サーバ側に送り返します。」と解説されていたが、wiresharkの例では、単に「\r\n」を返すだけだった。
「rspauth」は、「A2 = { ":", digest-uri-value }」のハッシュ値(HEX)と定義されている。digest-uri-valueは、変化無いので、
「rspauth=8252052849c337d2b318ef3b11d8db83」
となる。
一応、肯定応答を記事のように作成してみた(base64)
「dXNlcm5hbWU9ImluZm8uZXhhbXBsZS5jb20iLHJlYWxtPSJleGFtcGxlLmNvbSIsbm9uY2U9IkpRTUt0ZGdiRWhNcmE0R2RBWW1BalE9PSIsY25vbmNlPSJsV0Z7W1F1aVJqfV9MW1BXIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL214LmV4YW1wbGUuY29tIixyZXNwb25zZT04MjUyMDUyODQ5YzMzN2QyYjMxOGVmM2IxMWQ4ZGI4MyxjaGFyc2V0PXV0Zi04」
base64デコード値
「username="info.example.com",realm="example.com",nonce="JQMKtdgbEhMra4GdAYmAjQ==",cnonce="lWF{[QuiRj}_L[PW",nc=00000001,qop=auth,digest-uri="smtp/mx.example.com",response=8252052849c337d2b318ef3b11d8db83,charset=utf-8」
(3)DIGEST-MD5のチャレンジレスポンス生成スクリプト
macOSで動作するperlスクリプトを作成してみた
(4)DIGEST-MD5のチャレンジレスポンス生成スクリプトで接続確認
レスポンス応答は、RETURN KEYで応答した。レスポンス応答を「今更ながら DIGEST-MD5 SMTP認証」記載に従って応答(ResponseAuthを送信)し、認証が成功する事を確認した。
平文パスワードを送信する必要がある「LOGIN」と「PLAIN」認証だけにするとは、どの様な「セキュリティ」対策なのだろうか?
「LOGIN」「PLAIN」認証より「CRAM-MD5」認証の方がよほどセキュリティが高い気がする。
「DIGEST-MD5」認証とか「OAUTH」認証とかを加えるなら理解出来るが、、、
今更だが、Postfixメールサーバーで「CRAM-MD5」と「DIGEST-MD5」の認証をsmtpコマンドレベルで確認してみた。
(1)CRAM-MD5
「CRAM-MD5」は、検索するとコマンドレベルでの確認事例が出てくる。
smtp接続後、コマンドで「auth cram-md5」で認証方法を提示すると「base64でエンコードされたチャレンジ文字列」応答がある。
チャレンジ文字列をアカウントのパスワードをkeyとしてHMAC-MD5でhashコードを生成する。
ユーザIDとhashコードをスペース連結した文字列をbase64エンコードしてチャレンジ応答とする。
「CRAM-MD5でSMTP認証テスト」を参考にして掲載のシェルスクリプトをmacOS Big Sur(11.7.10)で実行したがエラーとなる。
cram-md5.sh
#!/bin/bash
if [ $# -ne 3 ]; then
echo "構文:"
echo "cram-md5.sh [ユーザID] [パスワード] [Base64コード]"
exit 1
fi
USERID=${1:?}
PASSWD=${2:?}
WORD=${3:?}
function hmac-md5() {
CODE=${1:?}
KEY=${2:?}
# normal openssl require awk because the output is (stdin)= 'result'
# echo "$CODE" | base64 -d | openssl dgst -md5 -hmac "$KEY" | awk '{print $2}'
# openssl in macOS Big Sur
echo -n "$CODE" | base64 -d | openssl dgst -md5 -hmac "$KEY"
}
echo
echo "出力:"
echo "以下のコードを貼り付けてください"
echo
echo -n "${USERID}" $(hmac-md5 "${WORD}" "${PASSWD}") | base64 | tr -d '\n'
echo
echo
exit 0
Debian10には、「/usr/bin/gen-auth」がインストールされていた。
/usr/bin/gen-auth
gen-auth cram-md5 someone@somehost.plala.or.jp himitsunokotoba PDk1NTA5Njk2LjM0NjA3MDhAc29tZWhvc3QucGxhbGEub3IuanA+
c29tZW9uZUBzb21laG9zdC5wbGFsYS5vci5qcCA5MTY4ZmZmNWZlNGRiMmEwMDUzN2NlYmI4OGQ5ODg3MA==
echo -n "c29tZW9uZUBzb21laG9zdC5wbGFsYS5vci5qcCA5MTY4ZmZmNWZlNGRiMmEwMDUzN2NlYmI4OGQ5ODg3MA==" | base64 -d
someone@somehost.plala.or.jp 9168fff5fe4db2a00537cebb88d98870
$ openssl s_client -connect somehost.plala.or.jp:587 -starttls smtp -quiet
250 CHUNKING
auth cram-md5
334 PDk1NTA5Njk2LjM0NjA3MDhAc29tZWhvc3QucGxhbGEub3IuanA+
c29tZW9uZUBzb21laG9zdC5wbGFsYS5vci5qcCA5MTY4ZmZmNWZlNGRiMmEwMDUzN2NlYmI4OGQ5ODg3MA==
235 2.7.0 Authentication successful
quit
221 2.0.0 Bye
(2)DIGEST-MD5
DIGEST-MD5(RFC2831)を検索してもコマンドレベルでの確認事例を見つけられなかった。「今更ながら DIGEST-MD5 SMTP認証」が詳細にプロトコルを記載していた。これを参考にDIGEST-MD5を確認してみた(「今更ながら CRAM-MD5 SMTP認証」も参考になった)。
「今更ながらDIGEST-MD5 SMTP認証」には、パラメータ類とhash計算結果が書かれているのでmacOSのopensslを使い計算式をシュミレートしてみた。同記事 「2)レスポンスMD5ハッシュの生成」で式
A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
":", nonce-value, ":", cnonce-value }
$ echo -n "info.example.com:example.com:userpassword" | openssl dgst -md5
0e50b2e5e12c4b08c865045a4bb780d1
md5a1x=`echo -n "info.example.com:example.com:userpassword" | openssl dgst -md5 -binary`
echo -n $md5a1x | od -t x1
0000000 0e 50 b2 e5 e1 2c 4b 08 c8 65 04 5a 4b b7 80 d1
0000020
echo -n "$md5a1x:JQMKtdgbEhMra4GdAYmAjQ==:lWF{[QuiRj}_L[PW" | openssl dgst -md5
7ab35907e69a040ad638cb3cd61ba9ff
示されたハッシュ値は「46 a5 d6 cc c1 56 d8 ca 8d a9 70 72 3d 45 5d 17」
「H( { username-value, ":", realm-value, ":", passwd } )」を「HEX( H( { username-value, ":", realm-value, ":", passwd } ) )」としてハッシュ値を確認もしてみた。
$ md5a1x=`echo -n "info.example.com:example.com:userpassword" | openssl dgst -md5`
$ echo $md5a1x
0e50b2e5e12c4b08c865045a4bb780d1
$ echo -n "$md5a1x:JQMKtdgbEhMra4GdAYmAjQ==:lWF{[QuiRj}_L[PW" | openssl dgst -md5
352faec92f18b88e728c0ae086ead7f5
同記事「2-2) A2 データ列の生成」の式
A2 = { "AUTHENTICATE:", digest-uri-value }は
echo -n "AUTHENTICATE:smtp/mx.example.com" | openssl dgst -md5
e7280b0554e7e6636bd6a32ec6d5d2cf
smtpコマンドデータを検証するため、XPS8300上のDebian10にpostfixを稼働させ、VAIO上のDebian10を使い、DIGEST-MD5認証でメール送信する設定をしたEvolutionメールツールで送信を行い、Wiresharkでsmtpコマンドをキャプチャして確認した。
auth digest-md5
334 ---challenge(base64)
--- response(base64)
334 ---- rspauth(base64)
\r\n
235 2.7.0 Authentication successful
base64デコードしてnonce値、realm値を得る。
その他のパラメータは「qop=auth,charset=utf-8,algorithm=md5-sess」。charsetの値は、「"」無し。
responseデータからcnonce値、digest-uri値を得て、opensslで確認すると、式H( { username-value, ":", realm-value, ":", passwd }は、md5バイナリー値にnonce値とcnonce値を":"で結合したデータがA1となる事が判った。
記事のA1ハッシュ値は、
「46 a5 d6 cc c1 56 d8 ca 8d a9 70 72 3d 45 5d 17」
でなく
「7a b3 59 07 e6 9a 04 0a d6 38 cb 3c d6 1b a9 ff」
となる。従って、「2-3) response ハッシュ値の算出」のハッシュ値は、
「9a ac 0f 21 5f 22 9c f8 20 83 b8 64 72 a2 78 8d」
となる。
「KD は2つの文字列を':'で連結の意」と注釈されているが、「RFC2617の3.2.1 The WWW-Authenticate Response Header」の解説で結合後にハッシュ化した値とされている。
「2-4) サーバに返すレスポンスデータ列の生成(base64)」は、
「dXNlcm5hbWU9ImluZm8uZXhhbXBsZS5jb20iLHJlYWxtPSJleGFtcGxlLmNvbSIsbm9uY2U9IkpRTUt0ZGdiRWhNcmE0R2RBWW1BalE9PSIsY25vbmNlPSJsV0Z7W1F1aVJqfV9MW1BXIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL214LmV4YW1wbGUuY29tIixyZXNwb25zZT05YWFjMGYyMTVmMjI5Y2Y4MjA4M2I4NjQ3MmEyNzg4ZCxjaGFyc2V0PXV0Zi04」
となる。
base64デコード値は、
「username="info.example.com",realm="example.com",nonce="JQMKtdgbEhMra4GdAYmAjQ==",cnonce="lWF{[QuiRj}_L[PW",nc=00000001,qop=auth,digest-uri="smtp/mx.example.com",response=9aac0f215f229cf82083b86472a2788d,charset=utf-8」
(*charset=の値は、「"」でクォートしていない)
記事の「3)認証確認」で「クライアント側は、肯定応答をするために同じように A2 データ列を上記のものに変更したレスポンスハッシュ値を算出し、サーバ側に送り返します。」と解説されていたが、wiresharkの例では、単に「\r\n」を返すだけだった。
「rspauth」は、「A2 = { ":", digest-uri-value }」のハッシュ値(HEX)と定義されている。digest-uri-valueは、変化無いので、
「rspauth=8252052849c337d2b318ef3b11d8db83」
となる。
一応、肯定応答を記事のように作成してみた(base64)
「dXNlcm5hbWU9ImluZm8uZXhhbXBsZS5jb20iLHJlYWxtPSJleGFtcGxlLmNvbSIsbm9uY2U9IkpRTUt0ZGdiRWhNcmE0R2RBWW1BalE9PSIsY25vbmNlPSJsV0Z7W1F1aVJqfV9MW1BXIixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL214LmV4YW1wbGUuY29tIixyZXNwb25zZT04MjUyMDUyODQ5YzMzN2QyYjMxOGVmM2IxMWQ4ZGI4MyxjaGFyc2V0PXV0Zi04」
base64デコード値
「username="info.example.com",realm="example.com",nonce="JQMKtdgbEhMra4GdAYmAjQ==",cnonce="lWF{[QuiRj}_L[PW",nc=00000001,qop=auth,digest-uri="smtp/mx.example.com",response=8252052849c337d2b318ef3b11d8db83,charset=utf-8」
(3)DIGEST-MD5のチャレンジレスポンス生成スクリプト
macOSで動作するperlスクリプトを作成してみた
digest-md5.pl
#!/usr/bin/perl
# digest-md5 smtpfqdn userid password challenge
#
use Digest::MD5 qw(md5 md5_hex md5_base64);
use MIME::Base64;
$fqdnhostnm=$ARGV[0];
$userid=$ARGV[1];
$password=$ARGV[2];
$chleg=decode_base64($ARGV[3]);
#----
$chleg =~ /nonce="([^"]*)"/; $nonce=$1;
$chleg =~ /realm="([^"]*)"/; $realm=$1;
$chleg =~ /qop="([^"]*)"/; $qop=$1;
$chleg =~ /charset=([^,]*)/; $charset=$1;
$chleg =~ /algorithm=([^,]*)/; $algorithm=$1;
#----
$cnonce=`openssl rand -base64 16`;
chomp $cnonce;
$md5a1=md5_hex(md5("$userid:$realm:$password"),":$nonce:$cnonce");
$digesturi="smtp/$fqdnhostnm";
$md5a2=md5_hex("AUTHENTICATE:$digesturi");
$md5a2r=md5_hex(":$digesturi");
$response=md5_hex("$md5a1:$nonce:00000001:$cnonce:$qop:$md5a2");
$rspauth=md5_hex("$md5a1:$nonce:00000001:$cnonce:$qop:$md5a2r");
$drx="username=\"$userid\",realm=\"$realm\",nonce=\"$nonce\",cnonce=\"$cnonce\",nc=00000001,qop=auth,digest-uri=\"$digesturi\",response=$response,charset=$charset";
$drxra="username=\"$userid\",realm=\"$realm\",nonce=\"$nonce\",cnonce=\"$cnonce\",nc=00000001,qop=auth,digest-uri=\"$digesturi\",response=$rspauth,charset=$charset";
print "Digest-Response: \n";
print encode_base64($drx,""),"\n";
printf("\nResponseAuth: %s\n",encode_base64($drxra,""));
(4)DIGEST-MD5のチャレンジレスポンス生成スクリプトで接続確認
openssl s_client -connect debian10.familyname:587 -starttls smtp -quiet
depth=1 CN = Private Network CA, OU = Root Certificate Authority, O = 1A72E31E-EA31-4576-AAE6-8C03A00091CD, C = JP
verify error:num=19:self signed certificate in certificate chain
verify return:0
250 CHUNKING
auth digest-md5
334 bm9uY2U9IncxNWdkdm1oeEhKMExwVmVjWWlXbzE3cDNWcmpNZCtXMVI4eWFmeXNHaEk9IixyZWFsbT0iZGViaWFuMTAuZmFtaWx5bmFtZSIscW9wPSJhdXRoIixjaGFyc2V0PXV0Zi04LGFsZ29yaXRobT1tZDUtc2Vzcw==
dXNlcm5hbWU9InNvbWVvbmVAc29tZWhvc3QucGxhbGEub3IuanAiLHJlYWxtPSJkZWJpYW4xMC5mYW1pbHluYW1lIixub25jZT0idzE1Z2R2bWh4SEowTHBWZWNZaVdvMTdwM1Zyak1kK1cxUjh5YWZ5c0doST0iLGNub25jZT0iQUtFZ0kveEpPRVNTMXNJS0MwRXNhZz09IixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL2RlYmlhbjEwLmZhbWlseW5hbWUiLHJlc3BvbnNlPWEwMGYyN2I5NDNjZDk0Nzg4MWE1YzIwNTc0MDlmYWY3LGNoYXJzZXQ9dXRmLTg=
334 cnNwYXV0aD03NGI5Nzg1ZDkxNmUzYTYwMTcxODJhMWZjMGE1MWM2Mg==
--<RETURN KEY>--
235 2.7.0 Authentication successful
./digest-md5.pl debian10.familyname someone@somehost.plala.or.jp himitunokagi bm9uY2U9IncxNWdkdm1oeEhKMExwVmVjWWlXbzE3cDNWcmpNZCtXMVI4eWFmeXNHaEk9IixyZWFsbT0iZGViaWFuMTAuZmFtaWx5bmFtZSIscW9wPSJhdXRoIixjaGFyc2V0PXV0Zi04LGFsZ29yaXRobT1tZDUtc2Vzcw==
Digest-Response:
dXNlcm5hbWU9InNvbWVvbmVAc29tZWhvc3QucGxhbGEub3IuanAiLHJlYWxtPSJkZWJpYW4xMC5mYW1pbHluYW1lIixub25jZT0idzE1Z2R2bWh4SEowTHBWZWNZaVdvMTdwM1Zyak1kK1cxUjh5YWZ5c0doST0iLGNub25jZT0iQUtFZ0kveEpPRVNTMXNJS0MwRXNhZz09IixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL2RlYmlhbjEwLmZhbWlseW5hbWUiLHJlc3BvbnNlPWEwMGYyN2I5NDNjZDk0Nzg4MWE1YzIwNTc0MDlmYWY3LGNoYXJzZXQ9dXRmLTg=
ResponseAuth: dXNlcm5hbWU9InNvbWVvbmVAc29tZWhvc3QucGxhbGEub3IuanAiLHJlYWxtPSJkZWJpYW4xMC5mYW1pbHluYW1lIixub25jZT0idzE1Z2R2bWh4SEowTHBWZWNZaVdvMTdwM1Zyak1kK1cxUjh5YWZ5c0doST0iLGNub25jZT0iQUtFZ0kveEpPRVNTMXNJS0MwRXNhZz09IixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJzbXRwL2RlYmlhbjEwLmZhbWlseW5hbWUiLHJlc3BvbnNlPTc0Yjk3ODVkOTE2ZTNhNjAxNzE4MmExZmMwYTUxYzYyLGNoYXJzZXQ9dXRmLTg=