Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Net::SMTP

 Net::SMTP

E4763acee4cf0377437f614fc372a061?s=128

とみたまさひろ

November 28, 2020
Tweet

Transcript

  1. Net::SMTP Net::SMTP Nagano.rb #6 2020-11-28 とみたまさひろ 1

  2. 自己紹介 自己紹介 とみたまさひろ ←投げ銭はこちら MySQL / メール / 文字化け 富士通クラウドテクノロジーズ勤務

    https://twitter.com/tmtms https://tmtms.hatenablog.com https://zenn.dev/tmtms 2
  3. SMTP SMTP Simple Mail Transfer Protocol (1982) → (2001) →

    (2008) メールメッセージ形式は → → ポート番号は 25 (smtp) RFC 821 2821 5321 RFC 822 2822 5322 3
  4. % nc smtp.example.com 25 ← 220 smtp.example.com ESMTP Postfix →

    EHLO client.example.net ← 250-smtp.example.com 250-PIPELINING 250-SIZE 102400000 250-VRFY 250-ETRN 250-STARTTLS 250-AUTH DIGEST-MD5 NTLM CRAM-MD5 PLAIN LOGIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250 SMTPUTF8 4
  5. → MAIL FROM:<sender@example.com> ← 250 2.1.0 Ok → RCPT TO:<rcpt1@example.com>

    ← 250 2.1.5 Ok → RCPT TO:<rcpt2@example.com> ← 250 2.1.5 Ok → DATA ← 354 End data with <CR><LF>.<CR><LF> → From: sender@example.com To: rcpt1@example.com Cc: rcpt2@example.com Subject: test message body . ← 250 2.0.0 Ok: queued as F074F9FB0E → QUIT ← 221 2.0.0 Bye 5
  6. 古き良き時代 6

  7. 迷惑メール 7

  8. 不正中継防止 不正中継防止 8

  9. 直接迷惑メールを送りつけてくるやつ対策 直接迷惑メールを送りつけてくるやつ対策 Outbound Port 25 Blocking (OP25B) 接続元のプロバイダーが外向けの25ポートをブロック プロバイダーが用意したメールサーバー経由でしか 外にメールを送れない

    受信と送信(中継)の分離 9
  10. 受信(MX) 受信(MX) TCP 25番ポート 自分宛のものしか受け付けない 一般ユーザーからは来ない 怪しいクライアントは拒否(設定次第) 送信者ドメインのSPFに登録されているか IPアドレスを逆引き&正引きして 元のIPアドレスになるか

    EHLO名がDNS上に存在しているか 10
  11. 送信(中継) 送信(中継) TCP 587番ポート(submission) TCP 465番ポート(smtps, submissions) どこ宛でもOK 信頼できるクライアントからしか受け付けない ローカルネットワークのクライアント

    認証が通ったクライアント 11
  12. SMTP認証 SMTP認証 メール送信時に認証が必要な場合 PLAIN はユーザー名とパスワードを Base64 しただけの平文 CRAM-MD5 等を使えば Challenge-Response

    方式の暗号化もできるけど サーバー内に平文のパスワードを保持しておかないといけないのがイマイチ → AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ= ← 235 2.7.0 Authentication successful 12
  13. 通信暗号化(STARTTLS) 通信暗号化(STARTTLS) SMTP 接続後に STARTTLS 命令を発行すると それ以降 TLS での暗号化通信になる (465番ポートは接続時からTLS)

    ← 220 smtp.example.com ESMTP Postfix → EHLO client.example.net ← 250-smtp.example.com 250-STARTTLS ... → STARTTLS ← 220 2.0.0 Ready to start TLS --- ここから TLS 通信 --- → EHLO client.example.net ← 250-smtp.example.com ... → AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ= ← 235 2.7.0 Authentication successful 13
  14. 暗号化通信は手動ではできないので openssl を使う % openssl s_client -connect smtp.example.com:587 -starttls smtp

    ...STARTTLS まで自動でやってくれる... --- ここから TLS 通信 --- → EHLO client.example.net ← 250-smtp.example.com ... 14
  15. TLS証明書の検証 TLS証明書の検証 オレオレ証明書とか期限切れ証明書はエラーにしたい % openssl s_client -connect smtp.example.com:587 -starttls smtp

    -verify_return_error ... Verification error: certificate has expired --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 10 (certificate has expired) --- % 15
  16. 証明書ホスト名の検証 証明書ホスト名の検証 証明書が正しくても自分がアクセスしてるサーバー用の 証明書じゃないかもしれない 接続先に指定した文字列とは異なるサーバー名を 使いたいこともある(テストとかで) % openssl s_client -connect

    smtp.example.com:587 -starttls smtp -verify_return_error -verify_hostname smtp.example.com % openssl s_client -connect 192.168.11.22:587 -starttls smtp -verify_return_error -verify_hostname smtp.example.com 16
  17. ここから Ruby の話 ここから Ruby の話 17

  18. Net::SMTP Net::SMTP めっちゃ簡単 require 'net/smtp' Net::SMTP.start('smtp.example.com', 25) do |smtp| smtp.send_message(<<EOS,

    'sender@example.com', 'rcpt1@example.com', 'rcpt2@example.com') From: sender@example.com To: rcpt1@example.com Cc: rcpt2@example.com Subject: test message body EOS end 18
  19. SMTP認証 SMTP認証 ユーザー名とパスワードを指定したいだけなのに EHLO 名を書かないといけないのがイマイチ 認証が必要なのは送信サーバーに送信するとき その場合は EHLO 名は重要ではないはず デフォルトだとTLSじゃないんだけど

    デフォルトの認証方式は PLAIN = 平文 Net::SMTP.start('smtp.example.com', 587, 'client.example.net', 'username', 'password') do |smtp| ... end 19
  20. STARTTLS STARTTLS Net::SMTP は start よりも前に enabel_starttls が必要なのがイマイチ smtp =

    Net::SMTP.new('smtp.example.com', 587) smtp.enable_starttls smtp.start('client.example.com', 'username', 'password') do ... end 20
  21. TLS(465番ポート) TLS(465番ポート) enabel_starttls じゃなくて enable_tls enable_starttls と enable_tls 両方指定するとエラー smtp

    = Net::SMTP.new('smtp.example.com', 465) smtp.enable_tls smtp.start('client.example.com', 'username', 'password') do ... end 21
  22. 証明書の検証 証明書の検証 デフォルトでは検証しない かなりダメな感じになってきた… smtp = Net::SMTP.new('smtp.example.com', 587) context =

    OpenSSL::SSL::SSLContext.new context.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER) smtp.enable_starttls(context) smtp.start('client.example.com', 'username', 'password') do ... end 22
  23. 証明書ホスト名の検証 証明書ホスト名の検証 デフォルトでは証明書の検証をしないのに なぜかホスト名の検証だけはしてる(バグっぽい) Net::SMTP は常に #start() の第1引数の文字列を使うので別のサーバ ー名を使うことはできない smtp

    = Net::SMTP.new('192.168.11.22', 587) context = OpenSSL::SSL::SSLContext.new context.set_params(verify_mode: OpenSSL::SSL::VERIFY_PEER) smtp.enable_starttls(context) smtp.start('client.example.com', 'username', 'password') #=> hostname "192.168.11.22" does not match the server # certificate (OpenSSL::SSL::SSLError) 23
  24. ということで の コミット権をもらった Net::SMTP は Ruby 2.7 から Gem になってる

    https://github.com/ruby/net-smtp/ 24
  25. キーワード引数化 キーワード引数化 ↓ EHLO名を指定しなくても認証情報を指定可能 Net::SMTP.start(hostname, port, helo_name, username, password, authtype)

    Net::SMTP.start(hostname, port, helo: helo_name, user: username, password: password, authtype: authtype) 25
  26. デフォルトで STARTTLS を使用 デフォルトで STARTTLS を使用 非互換! 非互換! サーバーが対応していれば自動的に STARTTLS

    を使用 Net::SMTP.start(hostname, port) { ... } → EHLO client.example.net ← 250-smtp.example.com 250-PIPELINING 250-SIZE 102400000 250-VRFY 250-ETRN 250-STARTTLS 250-AUTH DIGEST-MD5 NTLM CRAM-MD5 PLAIN LOGIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250-DSN 250 SMTPUTF8 26
  27. STARTTLS を使用したくない場合はちょっと面倒 smtp = Net::SMTP.new(hostname, port) smtp.disable_starttls smtp.start { ...

    } 27
  28. デフォルトでTLS証明書を検証 デフォルトでTLS証明書を検証 非互換! 非互換! 検証したくない場合(テストとか)は tls_verify: false を指定 Net::SMTP.start(hostname, port,

    tls_verify: false) { ... } 28
  29. デフォルトでホスト名を検証 デフォルトでホスト名を検証 これは今までどおり 証明書を検証しない時はホスト名も検証しない(バグ修正) 接続用の名前と異なるホスト名で検証したい場合は tls_hostname で指定 Net::SMTP.start('192.168.11.22', 587, tls_hostname:

    'smtp.example.com') { ... } 29
  30. net-smtp gem net-smtp gem Ruby 2.7 の人はこれで使える 正当な TLS 証明書を持ってないサーバーへの接続が

    エラーになる可能性あり オレオレ証明書とか % gem install net-smtp 30
  31. 順調にいけば Ruby 3.0 に入るかな 順調にいけば Ruby 3.0 に入るかな 31

  32. おわり おわり 32