天天看點

MIME郵件面面觀

此文部分出處為VC知識庫,您可以查閱,部分為本人添加,沒有版權問題。涉及本人言論無需注明出處

Q 什麼是MIME?什麼是MIME郵件?

A MIME, 全稱為“Multipurpose Internet Mail Extensions”, 比較确切的中文名稱為“多用途網際網路郵件擴充”。它是目前廣泛應用的一種電子郵件技術規範,基本内容定義于RFC 2045-2049。

自然,MIME郵件就是符合MIME規範的電子郵件,或者說根據MIME規範編碼而成的電子郵件。

在MIME出台之前,使用RFC 822隻能發送基本的ASCII碼文本資訊,郵件内容如果要包括二進制檔案、聲音和動畫等,實作起來非常困難。MIME提供了一種可以在郵件中附加多種不同編碼檔案的方法,彌補了原來的資訊格式的不足。實際上不僅僅是郵件編碼,現在MIME經成為HTTP協定标準的一個部分。

下面舉幾個MIME郵件的例子,讓我們先對MIME編碼的格式有個直覺的印象。例1是最簡單的,隻帶純文字正文,基本上就是RFC 822格式;例2複雜一些,包含純文字和超文本正文;例3是最複雜的,包含純文字正文、超文本正文、内嵌資源和檔案附件。其中,行号和行号後的空格是為了分析友善而另外加的,“... ... ... ...”表示此處省略了大段編碼。

例1

例2

例3

Q 在開始研究MIME郵件的時候,如何得到這樣的源碼?

A 一些功能比較完善的郵件用戶端軟體,如微軟的Outlook Express,國産的Foxmail等,都提供了檢視和儲存郵件源碼(原始資訊)的功能。在Foxmail中,選擇郵件清單右鍵菜單的“原始資訊”進行檢視,主菜單的“檔案-導出”進行儲存。在Outlook Express中,對應的操作分别是“屬性”和“另存為”。所儲存的.eml檔案,可以調用這些程式打開。

Q 請介紹一下MIME郵件的組成?

A 總體來說,MIME消息由消息頭和消息體兩大部分組成。現在我們關注的是MIME郵件,是以在以下的讨論中姑且稱“消息”為“郵件”。在上面的例子中,例1的1-6行,例2的1—8行,例3的1-18行,是郵件頭;例1的8—9行,例2的10—82行,例3的20—3128行,是郵件體。郵件頭與郵件體之間以空行進行分隔,如例1的第7行,例2的第9行,例3的第19行。郵件頭中不允許出現空行。有一些郵件不能被郵件用戶端軟體識别,顯示的是原始碼,就是因為首行是空行。

郵件頭包含了發件人、收件人、主題、時間、MIME版本、郵件内容的類型等重要資訊。每條資訊稱為一個域,由域名後加“: ”和資訊内容構成,可以是一行,較長的也可以占用多行。域的首行必須“頂頭”寫,即左邊不能有空白字元(空格和制表符);續行則必須以空白字元打頭,且第一個空白字元不是資訊本身固有的,解碼時要過濾掉。如例2的7-8行,例3的4-5行,13-14行,分别屬于一個域。

郵件體包含郵件的内容,它的類型由郵件頭的“Content-Type”域指出。常見的簡單類型有text/plain(純文字)和text/html(超文本)。

例2和例3中出現的multipart類型,是MIME郵件的精髓。郵件體被分為多個段,每個段又包含段頭和段體兩部分,這兩部分之間也以空行分隔。常見的multipart類型有三種:multipart/mixed, multipart/related和multipart/alternative。從它們的名稱,不難推知這些類型各自的含義和用處。它們之間的層次關系可歸納為下圖所示:

可以看出,如果在郵件中要添加附件,必須定義multipart/mixed段;如果存在内嵌資源,至少要定義multipart/related段;如果純文字與超文本共存,至少要定義multipart/alternative段。什麼是“至少”?舉個例子說,如果隻有純文字與超文本正文,那麼在郵件頭中将類型擴大化,定義為multipart/related,甚至multipart/mixed,都是允許的。

multipart諸類型的共同特征是,在段頭指定“boundary”參數字元串,段體内的每個子段以此串定界。所有的子段都以“--”+boundary行開始,父段則以“--”+boundary+“--”行結束。段與段之間也以空行分隔。在郵件體是multipart類型的情況下,郵件體的開始部分(第一個“--”+boundary行之前)可以有一些附加的文本行,相當于注釋,解碼時應忽略。段間也可以有一些附加的文本行,不會顯示出來,如果有興趣,不妨驗證一下。

結合boundary定界和multipart層次關系圖,我們分析一下例2和例3的郵件體層次與段嵌套關系。

在例2中,10-12行是附加文本行,13-82行是multipart/alternative型的段,包含兩個子段:13-30行是純文字正文,32-79行是超文本正文。

在例3中,20-21行是附加文本行,22-3127行是multipart/mixed型的段,包含3個子段:22-171行是multipart/related段,173-1688行與1690-3125行是兩個附件。multipart/related段又包含兩個子段:27-61行是multipart/alternative段,63-169行是一個内嵌資源(圖檔)。multipart/alternative段又包含兩個子段:31-48行是純文字正文,40-59行是超文本正文。

例1隻有純文字正文,實際上屬于multipart層次關系圖中的一個特殊情況。如果非要避簡就繁,寫成下面的形式,也是完全符合MIME精神的。

Q 在郵件頭和段頭中,有哪一些常見的域?

A 在郵件頭中,有很多從RFC 822沿用的域名,MIME也增加了一些。常見的标準域名和含義如下

域名含義添加者Received傳輸路徑各級郵件伺服器Return-Path回複位址目标郵件伺服器Delivered-To發送位址目标郵件伺服器Reply-To回複位址郵件的建立者From發件人位址郵件的建立者To收件人位址郵件的建立者Cc抄送位址郵件的建立者Bcc暗送位址郵件的建立者Date日期和時間郵件的建立者Subject主題郵件的建立者Message-ID消息ID郵件的建立者MIME-VersionMIME版本郵件的建立者Content-Type内容的類型郵件的建立者Content-Transfer-Encoding内容的傳輸編碼方式郵件的建立者

非标準的、自定義域名都以X-開頭,例如X-Mailer, X-MSMail-Priority等,通常在接收和發送郵件的是同一程式時才能了解它們的意義。

在段頭中,大緻有如下一些域

域名含義Content-Type段體的類型Content-Transfer-Encoding段體的傳輸編碼方式Content-Disposition段體的安排方式Content-ID段體的IDContent-Location段體的位置(路徑)Content-Base段體的基位置

有的域除了值之外,還帶有參數。值與參數、參數與參數之間以“;”分隔。參數名與參數值之間以“=”分隔。如例3的28-29行,Content-Type域的值為“multipart/alternative”,此外有一個參數boundary,值為"----=_NextPart_002_007C_01C3115F.80DFC5E0"。又如例3的第176行,Content-Disposition域的值為“attachment”,此外有一個參數filename,值為“readme.doc”。

Q Content-Type以及它們的參數有哪些形式?

A Content-Type都是“主類型/子類型”的形式。主類型有text, image, audio, video, application, multipart, message等,分别表示文本、圖檔、音頻、視訊、應用、分段、消息等。每個主類型都可能有多個子類型,如text類型就包含plain, html, xml, css等子類型。以X-開頭的主類型和子類型,同樣表示自定義的類型,未向IANA正式注冊,但大多已經約定成俗了。如application/x-zip-compressed是ZIP檔案類型。在Windows中,系統資料庫的“HKEY_CLASSES_ROOT/MIME/Database/Content Type”内列舉了除multipart之外大部分已知的Content-Type。

關于參數的形式,RFC裡有很多補充規定,有的允許帶幾個參數,較為常見的有

主類型參數名含義textcharset字元集imagename名稱applicationname名稱multipartboundary邊界

其中字元集也能在Windows系統資料庫的“HKEY_CLASSES_ROOT/MIME/Database/Charset”内見到。

Q Content-Transfer-Encoding有哪些?有什麼特點?

A Content-Transfer-Encoding共有Base64, Quoted-printable, 7bit, 8bit, Binary等幾種。其中7bit是預設的編碼方式。電子郵件源碼最初設計為全部是可列印的ASCII碼的形式。非ASCII碼的文本或資料要編碼成要求的格式,如上面的三個例子。Base64, Quoted-Printable是在非英語國家使用最廣使的編碼方式。Binary方式隻具有象征意義,而沒有任何實用價值。

Base64将輸入的字元串或一段資料編碼成隻含有{'A'-'Z', 'a'-'z', '0'-'9', '+', '/'}這64個字元的串,'='用于填充。其編碼的方法是,将輸入資料流每次取6 bit,用此6 bit的值(0-63)作為索引去查表,輸出相應字元。這樣,每3個位元組将編碼為4個字元(3×8 → 4×6);不滿4個字元的以'='填充。有的場合,以“=?charset?B?xxxxxxxx?=”表示xxxxxxxx是Base64編碼,且原文的字元集是charset。如例3第7行"=?gb2312?B?wLbAtrXEzOwNCg==?="是由簡體中文“藍藍的天”編碼而成的。在段體内則直接編碼,适當時機換行,MIME建議每行最多76個字元。如例3的1697-3125行,是一個ZIP檔案的Base64編碼。

Quoted-printable根據輸入的字元串或位元組範圍進行編碼,若是不需編碼的字元,直接輸出;若需要編碼,則先輸出'=',後面跟着以2個字元表示的十六進制位元組值。有的場合,以“=?charset?Q?xxxxxxxx?=”表示xxxxxxxx是Quoted-printable編碼,且原文的字元集是charset。在段體内則直接編碼,适當時機換行,換行前額外輸出一個'='。如例3的44-59行,是HTML文本的Quoted-printable編碼。其中第45行“=C7=E7=C0=CA”原文是“晴朗”,因為“晴”的GB2312碼是C7E7,“朗”的GB2312碼是C0CA。第48、53、57行末尾隻有孤零零的'=',表示這是由編碼造成的軟回車,而非原文固有的。

近年來,國内多數郵件伺服器已經支援8bit方式,是以隻在國内傳輸的郵件,特别是在郵件頭中,可直接使用8bit編碼,對漢字不做處理。如果郵件要出國,還是老老實實地按Base64或Quoted-printable編碼才行。

Q 什麼是内嵌資源?它有哪些形式?

A 内嵌資源也是MIME的一個發光點,它能使郵件内容變得生動活潑、豐富多彩。可在郵件的multipart/related架構内定義一些與正文關聯的圖檔、動畫、聲音甚至CSS樣式和腳本的段。通常在HTML正文内,使用超級連結與内嵌資源相聯系。如在例3中,HTML正文53-54行,解碼後為

它指出用一個Content-ID為007901c3111c$72b978a0$0100007f@bluesky的圖檔作為背景(cid:xxxxxxxx也是一種超級連結)。而64-169行恰好就是這樣一個内嵌資源。

除了用Content-ID進行聯系外,還有另外一種常用形式:用普通超級連接配接和Content-Location。例如:

在HTML正文中,

對應的内嵌資源為

另外,

是等效的。

Q 郵件病毒如何利用附件和内嵌資源傳播?

A 有的郵件附件可能帶有病毒,容易了解。附件畢竟是檔案,也好預防,不輕易打開就是了。但内嵌資源是在浏覽郵件内容時就要通路的,若其中藏有病毒或惡意代碼,你在不知不覺中就中招了。如前兩年曾經在全球範圍内流行的Nimda病毒,功能性源碼如下:

它将一個可執行檔案作為資源嵌入了架構型頁面,卻聲明這段可執行代碼是波形聲音類型。由于當時微軟的IE(版本5.0及以下)存在重大安全漏洞,沒有檢查Content-Type與name的擴充名是否比對,于是就被輕易騙過了,緻使點選或打開郵件時自動運作了這個“readme.exe”,機器就感染上病毒。帶毒的機器利用位址簿向别人發送帶毒的郵件,一傳十,十傳百,Nimda蠕蟲大行其道。

縱觀曆史,病毒剛出來時是厲害,但沒有任何一種能夠持續肆虐下去。Nimda如此,SARS亦當如此。曰:“多難興邦,衆志成城”,又曰:“非典終将倒下,城市精神永存”,相信我們定能很快戰勝“非典”!

病毒庫更新是跟在新病毒屁股後進行的,不要過分依賴防毒軟體。一個良好的習慣是關閉郵件預覽功能,或者設定預覽純文字部分,先檢視郵件源碼,确信排除病毒嫌疑後再打開。對陌生人發來的帶超文本正文的郵件,尤其要當心。永遠不要在郵件用戶端軟體内直接打開附件。

Q 一些垃圾郵件采取隐藏發件人的方式,如何追查它們來自哪裡?

A 從上面的郵件頭域名表中可以看出,郵件的建立者可以掌握大部分的域的内容,但Received等域由各級伺服器自動添加,發件人是鞭長莫及。垃圾郵件一般采用了群發軟體發送,郵件頭的From域(發件人位址)可以任意僞造,甚至寫成收件人位址(收到了自己并沒有發過的垃圾郵件,氣憤吧?)。檢視Received域(傳輸路徑)鍊可以找到真正的出處。每個伺服器添加的Received語句都在郵件首,故最下面一個Received就包含了發件人所用的SMTP或HTTP伺服器,及最初的網關外部IP位址。

Receive語句的基本格式是:from A by B。A為發送方,B為接收方。例如:

原文:http://blog.csdn.net/wanfustudio/article/details/1773412

本文轉自 Tenderrain 51CTO部落格,原文連結:http://blog.51cto.com/tenderrain/1946853

繼續閱讀