Ruby Net::SMTP
Ruby Net::SMTP
Fujitsu 크라운 기술 회사 Advent Calendar 2020과FUJITSU Advent Calendar 2020 5일째 보도다.
회사의 부가 달력이지만 기사 내용은 회사와 상관이 없다.
이것은 nagano.rb #6에 발표된 단락이다.
SMTP
SMTP는 Simple Mail Transfer Protocol의 약칭으로 메일을 보내는 프로토콜입니다.
RFC 변경 사항:
RFC 821(1982년)
RFC 2821(2001년)
RFC 5321(2008년)
smtp
.텍스트 프로토콜이기 때문에 사람도
DATA
부터 .
까지의 형식은 RFC 5322)이라고 말할 수 있다.S: 220 smtp.example.com ESMTP Postfix</span>
C: EHLO client.example.net</span>
S: 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
C: MAIL FROM:<[email protected]>
S: 250 2.1.0 Ok
C: RCPT TO:<[email protected]>
S: 250 2.1.5 Ok
C: RCPT TO:<[email protected]>
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: From: [email protected]
To: [email protected]
Cc: [email protected]
Subject: test
message body
.
S: 250 2.0.0 Ok: queued as F074F9FB0E
C: QUIT
S: 221 2.0.0 Bye
틀린 사람이 많지만 MAIL FROM:
과<
사이에는 공백이 없다.또한 메일 주소는 <
>
로 묶어야 한다.실제로 많은 서버가 비어 있음
<
>
으로, 없어도 오류가 발생하지 않지만 간혹 프로토콜 위반으로 오류가 발생하는 서버도 있습니다.우편물을 수발하다.
예전에는 메일 서버가 누가 보낸 메일이든 정확한 발송 목적지로 전송되는 것 같았다.
그러나 발송자가 사칭되거나 스팸메일을 보내는 데 사용된다
지금
하지만 그렇다고 이 서버로 직접 보내는 사람은 대응할 수 없다.
공급업체 측 대책으로 아웃바운드 포트 25 블록킹(OP25B)을 도입했다.
공급업체가 외부를 향한 25개 포트를 차단했기 때문에 공급업체가 준비한 메일 서버를 통해서만 외부로 메일을 보낼 수 있다.
많은 공급업체들이 도입했기 때문에 공급업체가 속한 네트워크에서 외부 메일 서버에 직접 연결할 수 없다.
그리고 수신과 발송이 분리되었다.
받기(MX)
전송 (중계)
SMTP 인증
이메일을 보낼 때 인증이 필요한 경우 SMTP의 AUTH 명령을 사용합니다.
C: AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=
S: 235 2.7.0 Authentication successful
얼핏 알 수 없는 문자열이지만 PLAIN은 기본적으로 64개의 사용자 이름과 비밀번호의 명문일 뿐이다.PLAIN 이외의 인증 방식, 예를 들어 CRAM-DMD5 등도 Charlene-Response 방식으로 암호화할 수 있지만 서버 내에 명문으로 된 암호를 유지해야 하는 것은 좋지 않다.
뭐, 비밀번호만 암호화했어도 메일 본문은 명문이어서 훔쳐볼 수도 있어.
통신 암호화(STARTTLS)
그러니까 통신 경로를 암호화해.
SMTP 연결 후 STARTTLS 명령을 실행하면 TLS의 암호화 통신이 뒤따릅니다.
또한 465번 포트는 HTTPS와 같고 연결할 때부터 TLS 통신이다.
S: 220 smtp.example.com ESMTP Postfix
C: EHLO client.example.net
S: 250-smtp.example.com
250-STARTTLS ← EHLO の応答に STARTTLS が含まれてれば使用可
...
C: STARTTLS
S: 220 2.0.0 Ready to start TLS
--- ここから TLS 通信 ---
C: EHLO client.example.net
S: 250-smtp.example.com
...
C: AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=
S: 235 2.7.0 Authentication successful
암호화 통신은 수동으로 할 수 없으며 SMTP를 손으로 두드리려면openssl을 사용합니다.% openssl s_client -connect smtp.example.com:587 -starttls smtp
(STARTTLS まで自動でやってくれる)
--- ここから手で入力したものが TLS 通信でサーバーに送られる ---
C: EHLO client.example.net
S: 250-smtp.example.com
...
TLS 인증서 확인
기본적으로 오픈스sl은 인증서를 검증하지 않기 때문에 저도 인증서나 기한이 지난 증명서를 통과할 것입니다. 틀리고 싶으면
-verify_return_error
을 추가합니다.% 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)
---
%
인증서 호스트 이름 확인
인증서가 합법적일지라도 자신이 방문한 서버용 인증서가 아닐 수도 있다.
인증서의 호스트 이름을 확인하려면
-verify_hostname
를 추가합니다.% openssl s_client -connect smtp.example.com:587 -starttls smtp -verify_return_error \
-verify_hostname smtp.example.com
Ruby SMTP
다음은 본론.
Net::SMTP
Ruby에서 SMTP를 사용할 때
net/smtp
프로그램 라이브러리를 사용합니다.아주 간단하게 사용할 수 있어요.require 'net/smtp'
Net::SMTP.start('smtp.example.com', 25) do |smtp|
smtp.send_message(<<EOS, '[email protected]', '[email protected]', '[email protected]')
From: sender@example.com
To: rcpt1@example.com
Cc: rcpt2@example.com
Subject: test
message body
EOS
end
SMTP 인증
SMTP 인증을 사용할 수도 있습니다.
Net::SMTP.start('smtp.example.com', 587, 'client.example.net',
'username', 'password') do |smtp|
...
end
사용자 이름과 비밀번호만 지정하고 싶은데 EHLO 이름을 써야 하는 것은 좋지 않다.인증이 필요한 것은 서버가 메일을 보낼 때이기 때문에 EHLO 이름은 중요하지 않을 것이다.
기본 인증 방식은 PLAIN, 즉 명문이며 기본적으로 TLS를 사용하지 않습니다.😇
STARTTLS
STARTTLS도 간단하게 사용할 수 있지만
Net::SMTP.start
안 돼, 안 Net::SMTP.new
안 되는 부분은 약간 미묘한 느낌이에요.smtp = Net::SMTP.new('smtp.example.com', 587)
smtp.enable_starttls
smtp.start('client.example.com', 'username', 'password') do
...
end
465번 포트처럼 STARTTLS가 아닌 경우 enable_starttls
대신 TLS를 사용해야 합니다.smtp = Net::SMTP.new('smtp.example.com', 465)
smtp.enable_tls
smtp.start('client.example.com', 'username', 'password') do
...
end
또한 지정enable_tls
과 enable_starttls
두 개 모두 오류가 발생할 수 있습니다.인증서 검사
Net::SMTP는 기본적으로 TLS 인증서를 검증하지 않습니다.내 증명서나 기한이 지난 증명서도 통과할 수 있다.
인증서의 검증은 다음과 같다.
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
OpenSSL 라이브러리의 사용법을 알아야 하는데 상당히 나빠진 것 같은데...인증서 호스트 이름 확인
Net: SMTP는 기본적으로 인증서를 검증하지 않지만 호스트 이름을 검증하는 이상한 행동을 합니다.
그리고 항상
enable_tls
또는 .new()
의 첫 번째 매개 변수 문자열을 사용하기 때문에 테스트에서 다른 서버 이름을 사용할 수 없습니다.IP 주소로 연결하면 호스트 이름이 맞지 않으면 오류가 발생합니다.
smtp = Net::SMTP.new('192.168.1.2', 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.1.2" does not match the server
# certificate (OpenSSL::SSL::SSLError)
Net: SMTP 변경 사항
그래서 이 안 좋은 부분을 어떻게든 해결하려고 https://github.com/ruby/net-smtp/제출권을 받아서 많이 해봤어요.
키워드 매개 변수
매개변수가 키워드 매개변수화되었습니다.
Net::SMTP.start(hostname, port, helo_name, username, password, authtype)
↓Net::SMTP.start(hostname, port, helo: helo_name,
user: username, password: password, authtype: authtype)
EHLO 이름을 지정하지 않고도 인증 정보를 지정할 수 있습니다.Net::SMTP.start('smtp.example.com', 587,
user: 'username', password: 'password') do |smtp|
...
end
기본적으로 STARTTLS[호환되지 않음] 사용
서버가 지원하는 경우 STARTTLS가 자동으로 사용됩니다.
.start()
의 응답EHLO
이 있으면 특별히 지정하지 않아도 STARTTLS를 사용합니다.Net::SMTP.start(hostname, port) do |smtp|
...
end
하지만 STARTTLS를 사용하지 않으려면 좀 귀찮아요.smtp = Net::SMTP.new(hostname, port)
smtp.disable_starttls
smtp.start { ... }
테스트 환경과 같은 환경에서 인증서가 제대로 설정되지 않았지만STARTTLS
되돌아오는 환경에서 오류가 발생할 수 있습니다. 이것은 호환되지 않습니다.기본 인증 인증서[호환되지 않음]
인증서도 기본적으로 검증됩니다.이것도 나의 인증서 등을 사용하는 환경에서 발생한 오류이기 때문에 호환되지 않는다.
인증서를 검증하지 않으려면 키워드 인자
EHLO
가 추가되었습니다.Net::SMTP.start(hostname, port, tls_verify: false) { ... }
호스트 이름 확인
인증서를 검증하지 않을 때도 호스트 이름을 검증하는 오류 행위를 수정했습니다.
STARTTLS
시 호스트 이름을 확인하지 않습니다.접속에서 사용하는 이름과 다른 호스트 이름
tls_verify
을 확인하기 위해 키워드 매개 변수가 추가되었습니다.이렇게 써도 돼요.
Net::SMTP.start('192.168.1.2', 587, tls_hostname: 'smtp.example.com') { ... }
net-smtp gem
tls_verify: false
라이브러리는 Ruby2.7에서 Gem으로 바뀌었고 새로운 tls_hostname
를 사용하려면 다음과 같은 방법으로 사용할 수 있습니다.% gem install net-smtp
루비2.7 이전에는 젬이 설치되어 있어도 표준부가라이브러리net/smtp
가 사용될 것이다(표준부가라이브러리를 강제로 삭제한 경우net/smtp
도 사용할 수 없는 것은 아니다...)인증서의 검증 주위에 호환되지 않는 부분이 있으니 조심해서 사용하세요.
루비의 깃 창고도 넣었기 때문에 특별한 문제가 없다면 루비 3.0에서 이 신규
net/smtp
가 기준이다.
Reference
이 문제에 관하여(Ruby Net::SMTP), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/tmtms/articles/09aa7b7cd99f487f9dd3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)