SMTP協(xié)議介紹
SMTP(Simple Mail Transfer Protocol)即簡單郵件傳輸協(xié)議,郵件通過這個協(xié)議在MUA和MTA、MTA和MTA之間傳輸。有了第一篇的基礎(chǔ),相信大家也都了解到了,整個互聯(lián)網(wǎng)郵箱體系能夠正常運行的核心就是SMTP協(xié)議,這個協(xié)議好比郵箱界的 HTTP。有了 SMTP ,大家都遵守同一套的傳輸規(guī)范, 它保證了你發(fā)出去的郵件對方的MTA能收到,也保證了對方MTA發(fā)過來的郵件你可以收到。要保持你的郵箱系統(tǒng)能夠正常運作,甚至 POP3/IMAP 協(xié)議都不是必須的,如果你不需要通過客戶端收郵件的話,那么你其實可以通過登錄郵件服務(wù)器主機(jī)直接使用mail/mailx 或者mutt 這樣的命令行工具就可以進(jìn)行郵件的收發(fā)。我們現(xiàn)在最常用的webmail,如網(wǎng)易、gmail的網(wǎng)頁郵箱其實也沒有用到 POP3/IMAP協(xié)議(除非你需要配置客戶端)。
SMTP 也是基于 TCP 協(xié)議的,下面是命令和應(yīng)答清單:

感覺和http協(xié)議特別像有木有?甚至smtp報文中的頭部和http頭部也特別像,我只能說可能是同一幫人設(shè)計的。。郵件的報文格式封裝:

這里要注意的是報文的信封部分是不會體現(xiàn)在郵件內(nèi)容里面的。smtp協(xié)議是能夠直接通過telnet工具執(zhí)行命令的,下面我們就來實戰(zhàn)一下,加深理解
使用smtp服務(wù)器收郵件
我們這里假設(shè)有一個郵件服務(wù)商叫做 abc.mail(僅用于演示,非真實,下同),其他郵箱基本也都差不太多。首先通過nslookup命令找到mx記錄,查詢 abcmail用于收件的smtp服務(wù)器地址(一般叫Mail Exchanger,MX)
bob$ nslookup -type=mx abc.mail
abc.mail MX preference = 15, mail exchanger = mx6.abc.mail
abc.mail MX preference = 10, mail exchanger = mx2.abc.mail
abc.mail MX preference = 10, mail exchanger = mx4.abc.mail
abc.mail MX preference = 10, mail exchanger = mx3.abc.mail
abc.mail MX preference = 10, mail exchanger = mx1.abc.mail
abc.mail MX preference = 15, mail exchanger = mx5.abc.mail
我們選擇 mx1.abc.mail 這個服務(wù)器,直接telnet它的25端口,相關(guān)輸入的命令我寫在了后面的注釋里,實際執(zhí)行時要去掉:
bob % telnet mx1.abc.mail 25
Connected to mx1.abc.mail.
Escape character is '^]'.
220 mx.abc.mail ESMTP c16-20020a170902d49000b0019e25cff4f5si9627390plg.617 - gsmtp
helo app //通知smtp服務(wù)端開啟會話,helo 后面的內(nèi)容可以隨便輸入,還有一個增強(qiáng)型的命令ehlo,能夠返回更多服務(wù)端的內(nèi)容
250 mx.abc.mail at your service
mail from:<foobar@example.com> //發(fā)件人地址
250 2.1.0 OK c16-20020a170902d49000b0019e25cff4f5si9627390plg.617 - gsmtp
rcpt to:<foobar@abc.mail> //收件人地址
250 2.1.5 OK c16-20020a170902d49000b0019e25cff4f5si9627390plg.617 - gsmtp
data //開始郵件內(nèi)容編寫
354 Go ahead c16-20020a170902d49000b0019e25cff4f5si9627390plg.617 - gsmtp
from: foobar<foobar@example.com> //郵件首部
to: foobar<foobar@abc.mail> //郵件首部
subject: hello //郵件首部
this is text //郵件正文
. //郵件內(nèi)容以一個半角的.結(jié)束
550-5.7.28 [156.146.145.84 1] Our system has detected an unusual rate of
550-5.7.28 unsolicited mail originating from your IP address. To protect our
550-5.7.28 users from spam, mail sent from your IP address has been blocked.
20020a170902d49000b0019e25cff4f5si9627390plg.617 - gsmtp
不出意外的,這里出了意外了??。郵件被拒絕發(fā)送,原因是發(fā)件方MTA的ip地址未經(jīng)授權(quán)。一般來說,MX服務(wù)器默認(rèn)對方也是MTA,需要進(jìn)行各種安全認(rèn)證,我們是通過個人電腦連接上去,自然是通不過認(rèn)證了。似乎出師不利,但是也符合預(yù)期,目前各大郵箱的發(fā)送方身份認(rèn)證策略都設(shè)置的比較完善了,隨便設(shè)置發(fā)件人郵箱肯定是行不通了。這里我們先放一放,待自己的郵件服務(wù)器搭建完畢后再進(jìn)行測試。
使用smtp服務(wù)器發(fā)郵件
abc.mail 發(fā)送郵件的服務(wù)器為 smtp.abc.mail ,我們先來測試一下25端口:
bob% telnet smtp.abc.mail 25
Connected to smtp.abc.mail.
Escape character is '^]'.
220 smtp.abc.mail ESMTP d13-20020a170902b70d00b001a1d553de0fsm5958849pls.271 - gsmtp
ehlo app
250-smtp.abc.mail at your service,
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
250 smtp.abc.mail at your service
mail from:<foobar@abc.mail>
530 5.7.0 Must issue a STARTTLS command first. d13-20020a170902b70d00b001a1d553de0fsm5958849pls.271 - gsmtp
可以看到發(fā)件人需要進(jìn)行認(rèn)證,而認(rèn)證必須在tls連接中進(jìn)行,從而有效保護(hù)傳輸?shù)男畔踩?。telnet是不支持tls連接的,這里我們需要使用openssl工具來進(jìn)行安全連接:
openssl s_client -starttls smtp -connect smtp.abc.mail:587 -crlf -ign_eof
...
ehlo app
250-smtp.abc.mail at your service,
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
auth login
334 VXNlcm5hbWU6
Zm9vYmFy //用戶名base64編碼,演示用非真實
334 UGFzc3dvcmQ6
Zm9vYmFy //應(yīng)用專用密碼base64編碼,演示用非真實
235 2.7.0 Accepted
mail from:<foobar@abc.mail>
250 2.1.0 OK z14-20020a6553ce000000b00502f017657dsm5485604pgr.83 - gsmtp
rcpt to:<foobar@163.com>
250 2.1.5 OK z14-20020a6553ce000000b00502f017657dsm5485604pgr.83 - gsmtp
data
354 Go ahead z14-20020a6553ce000000b00502f017657dsm5485604pgr.83 - gsmtp
from: foobar<foobar@abc.mail>
to: foobar<foobar@163.com>
subject: helo
text
.
250 2.0.0 OK 1681052959 z14-20020a6553ce000000b00502f017657dsm5485604pgr.83 - gsmtp
587端口是smtp 的 starttls的端口(也可以使用25端口),可以看到建立tls連接后出現(xiàn)了auth命令。這里要注意的是,主流的各大郵箱已經(jīng)禁用在非安全設(shè)備上直接使用用戶名密碼的登錄方式,直接登錄會失敗。需要先在郵箱控制面板中生成應(yīng)用專用密碼,再進(jìn)行登錄。上述操作后,郵件發(fā)送成功,在163郵箱可以收到這封郵件了。
或者也可以使用下面這個命令通過465端口建立tls連接,效果是一樣的:
openssl s_client -connect smtp.abc.mail:465 -crlf -ign_eof
IMAP 協(xié)議介紹
IMAP協(xié)議, Internet Mail Access Protocal (交互式郵件訪問協(xié)議),是一個應(yīng)用層協(xié)議(端口是143)。IMAP協(xié)議比POP3協(xié)議復(fù)雜的多,也是按照C/S的工作方式,現(xiàn)在較新的版本是IMAP4。
與POP3協(xié)議類似,IMAP也是提供面向用戶的郵件收取服務(wù)。常用的版本是IMAP4。IMAP4改進(jìn)了POP3的不足,用戶可以通過瀏覽信件頭來決定是否收取、刪除和檢索郵件的特定部分,還可以在服務(wù)器上創(chuàng)建或更改文件夾或郵箱,它除了支持POP3協(xié)議的脫機(jī)操作模式 外,還支持聯(lián)機(jī)操作和斷連接操作。它為用戶提供了有選擇的從郵件服務(wù)器接收郵件的功能、基于服務(wù)器的信息處理功能和共享信箱功能。IMAP4的脫機(jī)模式不同于POP3,它不會自動刪除在郵件服務(wù)器上已取出的郵件,其聯(lián)機(jī)模式和斷連接模式也是將郵件服務(wù)器作為“遠(yuǎn)程文件服務(wù)器”進(jìn)行訪問,更加靈活方便。目前只使用POP3協(xié)議的郵箱已經(jīng)很少了,主流是IMAP,故本系列文章也主要以介紹IMAP協(xié)議為主。
imap同樣也是直接可以用命令行登錄的,下面我們來測試一下,因為目前主流郵件服務(wù)商都已經(jīng)禁用了非安全的143端口,我們直接通過 tls 端口 993 來登錄,需要輸入的命令行后面添加了注釋:
openssl s_client -connect imap.abc.mail:993 -crlf -ign_eof
a login foobar password //輸入用戶名密碼,每個命令前要輸入一個 a 作為開始
* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- SPECIAL-USE APPENDLIMIT=35651584
a OK foorbar@abc.mail authenticated (Success)
a list "" * //列出所有的郵箱
* LIST (\HasNoChildren) "/" "Call log"
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasNoChildren) "/" "Notes"
* LIST (\HasNoChildren) "/" "SMS"
* LIST (\HasNoChildren) "/" "Sent"
a OK Success
a select inbox //選擇inbox收件箱
* FLAGS (\Answered \Flagged \Draft \Deleted \Seen $Forwarded $NotPhishing $Phishing Old)
* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen $Forwarded $NotPhishing $Phishing Old \*)] Flags permitted.
* OK [UIDVALIDITY 5] UIDs valid.
* 6901 EXISTS
* 0 RECENT
* OK [UIDNEXT 6934] Predicted next UID.
* OK [HIGHESTMODSEQ 463331]
a OK [READ-WRITE] inbox selected. (Success)
上述命令使用用戶名+專用密碼登錄,列出郵箱列表,并選擇了inbox郵件箱。imap 命令比較多,這里就不一一列舉了,詳細(xì)命令可參考附件列表里的資料。smtp和imap的telnet(openssl)命令都非常重要,需要熟練掌握,這是我們后續(xù)自建服務(wù)器進(jìn)行調(diào)試的重要手段。
關(guān)于端口號
從上面的介紹大家也可以看出來,由于電子郵件存在的歷史非常長,使用的端口號也經(jīng)歷了不少階段,導(dǎo)致看起來有些混亂。最早大家都是明文傳輸,smtp使用25,imap使用143,還比較清楚。后來隨著互聯(lián)網(wǎng)的發(fā)展,大家意識到不能在網(wǎng)上裸奔了,加密很重要,于是誕生了smtps(465),imaps(993),這兩個端口使用的是ssl協(xié)議。再后來ssl協(xié)議也被淘汰了,取而代之的是tls協(xié)議,配套開發(fā)了starttls協(xié)議,可以復(fù)用原來的明文端口,即可以先連接smtp的25端口,根據(jù)服務(wù)端是否支持starttls,再判斷是否啟用加密連接,相對更加靈活。同時,由于25端口被客戶端廣泛用于發(fā)送垃圾郵件,所以客戶端(MUA)的starttls端口被定義成了587端口。再后來,大家又覺得其實端口不是重點,tls加密才是最重要的,所以現(xiàn)在的465和993都是同時支持ssl/tls的(ssl基本已不再使用),不管使用starttls還是普通tls,加密級別和安全性都是一樣的。
SMTP服務(wù)器分類
- 發(fā)件服務(wù)器:這個就是我們通常意義上了解的smtp服務(wù)器,域名形式通常為 smtp.域名,如smtp.163.com,smtp.gmail.com等,普通用戶接觸最多的是這類服務(wù)器,郵件客戶端里面配的一般也是這個地址。作用是接收郵件客戶端發(fā)送過來的郵件,并轉(zhuǎn)發(fā)到其他的郵件服務(wù)器中。早期使用的是25端口,近年來隨著安全傳輸要求的不斷提升,一般25端口已被禁用,以使用465(SSL/TLS)及587(STARTTLS)端口為主。為防止垃圾郵件的發(fā)送,通常都會做發(fā)件人的身份驗證,認(rèn)證通過后 smtp 服務(wù)器才會允許以該發(fā)件人身份向其他郵件服務(wù)器發(fā)送郵件。
- 收件服務(wù)器:一般叫 Mail Exchanger,MX服務(wù)器,地址可通過查詢 dns 的mx記錄獲取。一般只是在郵件服務(wù)商之間傳輸郵件時用到,普通用戶接觸不多。MX服務(wù)器通常只開通25端口,上面既可以跑明文流量,也可以跑加密流量(通過starttls命令聲明進(jìn)行加密傳輸)。MX服務(wù)器一般會校驗,收件人地址是本地用戶,才會接收。同時,由于是MTA之間的傳輸,MX一般也會對發(fā)送方的MTA進(jìn)行嚴(yán)格校驗(還記得文章開頭收郵件失敗嗎?),具體方式有SPF、DKIM、DMARC、rDNS等,后續(xù)在講郵件身份認(rèn)證時會講到。
一般來說,出于職責(zé)權(quán)限劃分及處理性能的考慮,大的郵件服務(wù)商都會把收件服務(wù)器和發(fā)件服務(wù)器分開單獨部署。自建的小站,用戶也沒幾個,合并部署問題也不大。
參考資料
1、SMTP協(xié)議介紹
https://blog.csdn.net/qq_35644234/article/details/68961603
2、測試imap服務(wù)器命令詳解
https://blog.csdn.net/sinat_37213335/article/details/88291078
3、細(xì)說電子郵件的端口號
https://taoshu.in/email-ports.html