検索
リンク
タグ
ASP.NET
.NET
ASP.NET MVC
F#
Visual Studio
Azure
ASP.NET Core
ライトニングトーク
Plone
AJAX
Selenium
C#
jQuery
ADO.NET Entity Framework
SQL Server
JavaScript
LINQ
WebMatrix
EFCore
Fizz-Buzz
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2023年 01月 2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 ファン
ブログジャンル
画像一覧
|
2023年 01月 30日
C# で、SMTP で、メールを送信するぞC# によるプログラミングにおける話。 ここ最近はメールを送信するプログラムを書く機会もめっきり無くなっていたのだが、久しぶりにそのようなプログラムを作成する機会が発生した。 メール送信のインフラストラクチャとしては、その筋では大手 (ですよね?) の SendGrid を選択。 SendGrid を使ってメール送信を行なう場合は、SendGrid が用意してくれている REST API に HTTP POST することで実装することも多いと思うが、今回は諸事情で、古典的な SMTP プロトコルを使ったメール送信を実装することとなった。 および、昨今は、C# で SMTP でメール送信をする場合は「MailKit を使いましょう」ということらしい。 「System.Net.Mail 名前空間の各種実装はもう更新されておらず、下位互換維持のために存在しているだけなので、他のモダンな実装に移行していきましょう」といったことが、下記リンク先の公式ドキュメントに記されている。 ただこちらも諸事情あって、今回は引き続き古典的な System.Net.SmtpClient クラスを一時採用することと相成った。 ということで、そのようなメール送信を行なう C# プログラム部分は下記のようになった。 using var smtpClient = new SmtpClient( host: "smtp.sendgrid.com", port: 587) { Credentials = new NetworkCredential( userName: "apikey", password: "SG....") }; 送信試験はいったんは成功したんだけど...とりあえず簡単にメールを試験送信して無事成功することを確認。その後にもういちどソースコードを眺めていると、ふと大事な設定漏れに気づいた。EnableSsl = true の設定をしていなかったのである。ということで、危ないところであったが、EnableSsl = true の設定を追加。 using var smtpClient = new SmtpClient(...) { Credentials = new NetworkCredential(...), EnableSsl = true // 👈 追加! }; どうせ開発用のキーだしそうそう心配したものではないと思いつつ、そうは言っても実際にインターネット経由で API キーを含む通信を平文で流してしまったのはそうなので、念のため API キーも再発行を済ませた。 その上で今一度メール送信を試験実行。 すると予期しないことに、実行時例外が発生してしまった。例外メッセージは下記のとおり。 Unhandled exception. System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch at CompleteHandshake(SslAuthenticationOptions) at SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean, Byte[], CancellationToken) at SslStream.AuthenticateAsClient(SslClientAuthenticationOptions sslClientAuthenticationOptions) at TlsStream.AuthenticateAsClient() at SmtpConnection.GetConnection(String host, Int32 port) at SmtpTransport.GetConnection(String host, Int32 port) at SmtpClient.Send(MailMessage message) at Program.<Main>$(String[] args) EnableSsl = true を設定する前はちゃんとメール送信できていたのに、EnableSsl = true を指定したとたん例外が発生するとはどういうこと? API キーを再発行したのが何かマズかった? それとも MailKit 使ってなかったのが敗因?? いろいろなことが頭の中を駆け巡る。 待て待て。こんなときはしっかりエラーメッセージを読むんだ。答えはそこに書いてあるはずだ。えーと、なになに、メッセージは「The remote certificate is invalid」と言っている。つまり、「リモート証明書が無効です」ということだ。ん? リモート証明書? それって SendGrid 側の証明書ってこと? それが無効ってどういうこと? SendGrid 側が何かやらかしたってこと? まさか証明書が期限切れになったのに更新してなかったみたいな初心者レベルのミスとかー!? 原因はやっぱり...待て待て、もういちど落ち着こう。大体こういうパターンは自分が書いたコード中に原因がある。そもそも SendGrid がそんなミスしていたら今頃 Twitter 上で盛大に盛り上がっているはずなのだ。 ということでもういちどソースコードを眺めて見る。 using var smtpClient = new SmtpClient( host: "smtp.sendgrid.com", port: 587) { ... 実は上記の中に一点、間違いがあるのだが、皆さんは気づいただろうか? 正しい実装は下記のとおり。 using var smtpClient = new SmtpClient( host: "smtp.sendgrid.net", port: 587) { ... そう、ホスト名のトップレベルドメインを間違っていたのだ。smtp.sendgrid.net としなければならないところを smtp.sendgrid.com としてしまっていたのだ。これはお恥ずかしい。「SendGrid への SMTP 経由での送信なんか、過去に何度も書いてたしー。ホスト名? ああ、アレでしょ、smtp で始まるやつでしょ、Gmail も smtp.gmail.com だったし」とナメくさって、ろくに公式ドキュメントを確認もせずに smtp.sendgrid.com って書いてしまったのが敗因であった。 なお、nslookup で、それぞれのホスト名 smtp.sendgrid.com と smtp.sendgrid.net を正引きしてみると、どちらも同じ IP アドレスが返る、っていうか、smtp.sendgrid.com は smtp.sendgrid.net へのエイリアス (CNAME) であった。 C:\> nslookup smtp.sendgrid.com 8.8.8.8 Server: dns.google Address: 8.8.8.8 Non-authoritative answer: Name: smtp.sendgrid.net Addresses: 52.220.95.193 13.114.210.107 Aliases: smtp.sendgrid.com C:\> nslookup smtp.sendgrid.net 8.8.8.8 Server: dns.google Address: 8.8.8.8 Non-authoritative answer: Name: smtp.sendgrid.net Addresses: 13.114.210.107 52.220.95.193 EnableSsl = false の場合、非セキュア接続の場合は証明書の確認や暗号化など行なわずに平文でやりとりするだけだ。その場合、smtp.sendgrid.com でも smtp.sendgrid.net でも、どちらのホスト名を指定しても接続する先の IP エンドポイントに違いがないため、そのまま SMTP でメール送信できていたようだ。 いっぽうで EnableSsl = true としてセキュア接続を使用する場合は、通信相手のサーバーの証明書を確認する必要が出てくる。ここで証明書はその発行対象であるコモンネームを含むので、発行対象とは異なるコモンネームになるホスト名でセキュア接続を試みると、証明書の検証がエラーとなる。SendGrid としては公式ドキュメントサイトで説明しているとおり、SMTP ホスト名は「smtp.sendgrid.net を設定してください」とあるので、この SMTP サーバーの証明書は「smtp.sengrid.net」をコモンネームとして取得しているはずだ。なのに自分のプログラムは「smtp.sendgrid.com」なんていうホスト名で接続しようとしたものだから、たしかに IP アドレスは正しく解決できて TCP ポート 587 番で通信は開始したけど、結果、先の例外メッセージにある「リモート証明書が無効です」はこういう訳だったのだ。 おわりに教訓。 自分が作ったプログラムでトラブルがあると、その原因を、接続先のサービスや、はてはクラスライブラリやコンパイラのバグに求めたくなることがあるかもしれない。でも多くの場合は、自分のプログラムに原因があることがほとんどだ (と思う。自分の身の上では。)
by developer-adjust
| 2023-01-30 20:35
| .NET
|
Comments(0)
|
ファン申請 |
||