改造Nginx,讓郵件系統也支持雙因子驗證
*本文原創作者:idapro,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
起 因
最近在研究雙因子認證的時候突然想到:能不能在郵件系統中應用雙因子驗證呢?作為一個有了想法就想落地的四有好少年,我決定試試。
受制於文化程度和錢包鼓起程度,我在本地用Exchange搭建了一套模擬環境(當然不是正版)。大家不要學習我這種做法,我們要支持正版。嗯嗯。
整個改造分為兩塊進行,一塊是Web端進行雙因子驗證支持,這塊不是難點,通過反向代理服務可以迅速解決。但是,在進行其他協議(例如SMTP、POP3、Exchange)改造的時候,發現事情並沒有想像中的這麼簡單。
技術選型遇到的一點問題
作為一條萬年沒人權的Web狗,突然間給自己設定了一個這麼艱巨的任務,頓時懷疑起了人生。翻了翻自己的技術棧,看來看去也就nginx可能也許大概好像能滿足要求,那麼久拿他來試試吧。
Nginx是個優秀的反向代理(負載均衡)工具,大多數人都對它在Web方嚮應用比較熟悉,但其實Nginx還支持對SMTP、IMAP、POP3這些郵件協議進行反向代理或者負載均衡。
官網文檔見https://www.nginx.com/resources/admin-guide/mail-proxy/
。
於是,按照官方文檔,寫了個配置文件:
然後再用世界上最好的語言PHP,寫一個auth_http的配套服務(這個內容下文會提到)。
跑一跑程序,成了,auth_http服務能夠正確收到客戶端提交的賬號密碼,可是遇到了兩個問題:
1、壓根兒沒動態
驗證碼出場的機會啊;
2、在SMTP協議的代理中,auth_http只能向nginx返回是否驗證通過的結果,nginx無法繼續將賬號密碼向後端節點傳遞,導致真實的節點無法驗證用戶身份。
(填寫完賬號密碼後,後端SMTP伺服器回應拒絕發送,從Nginx的日誌中的確發現我們完成了auth_http的認證)
對於問題1,其實SMTP/POP3/IMAP協議本身並沒有提供支持雙因子驗證的設計,谷歌採用的思路是設置一個隨機生成的靜態密碼與賬號綁定。大部分人和谷歌不太一樣,我們只能考慮在用戶名或密碼上動點手腳了:
這樣一來,我們在兼容原有協議的基礎上可以使郵件系統能夠完整地支持雙因子驗證。至於後一個問題,我翻遍nginx的文檔都沒有找到著解決方法,上谷歌一搜後,發現很久之前就有同仁們提出過類似疑惑:
http://serverfault.com/questions/726270/setting-up-nginx-to-proxy-adding-ssl-smtp-with-authentication
http://mailman.nginx.org/pipermail/nginx/2010-February/019028.html
看來是無解咯?
幸好Nginx是開源的軟體,我們還可以靠自己。
閱讀源碼,發現問題
要怒懟Nginx,首先得深入源碼。
Nginx用來處理Mail協議的代碼在src/mail/目錄下,我們主要關注ngx_mail_proxy_module.c和ngx_mail.h兩個文件。
這兩個文件大致定義了Nginx對協議的處理過程,其中SMTP協議長這樣:
(上圖省略了例如環境初始化、容錯、其他場景特殊處理等過程)
在原有的Nginx處理過程中,從auth_http得到返回數據後,程序跳過了與後端節點通信的過程,而僅設置了該session對應的後端節點後便不作處理。
所以在
官方文檔中,auth_http僅能響應Auth-Server、Auth-Port兩個頭部來指示Nginx的後續操作,不具備向後端轉發認證信息的過程。
這時候,我們有兩種選擇:
1、後端SMTP伺服器不再進行登錄驗證,僅允許來自Nginx的即可;
2、改造Nginx,使得其支持SMTP的認證過程。
第一種方案當然可行,從Nginx的實現來看,甚至可以認為是推薦這麼操作的。但是,這個方案有天然的弱點,認證過程需要被解耦。也就是說,認證服務需要同時存在於郵件伺服器(IMAP/POP3需要用到)和auth_http中,兩者必須一致。
(Nginx源碼中,對IMAP已有相關實現)
在我看來這樣有點麻煩,在一些特殊的場景下可能會有適配問題,所以我決定改造Nginx,使得它能夠支持SMTP協議認證過程的轉發。
修改源碼,解決問題
修改源碼其實非常簡單,因為此前的IMAP、POP3中Nginx已經完成了相關的實現,只不過沒把它加入SMTP中。考慮到SMTP協議本身不是強認證的(即認證過程是可選的),所以不建議直接強硬地設置這個流程,應當通過配置項管理。
我把修改後的源代碼放到了全球最大的同性交友平台上(
idapro123/mail_protector),修改的內容並不多,大家不妨可以看看。
這裡簡要說明修改的內容:
1、在mail配置節中新增了一個可選配置項:smtp_auth_reproxy on | off (default),該配置項的作用是用來控制是否將客戶端的AUTH過程傳遞給後端伺服器進行驗證;
2、調整了SMTP協議的代理邏輯,當smtp_auth_reproxy被顯式置為on的時候,會向後端伺服器轉發auth_http響應頭中的Auth-User和Auth-Pass欄位。
(修改後的代碼中,同樣增加了向後端伺服器轉發認證請求的過程)
這樣一來,我們上面提到的問題都不復存在了,剩下的,就是如何去寫一個雙因子驗證服務。
我同樣將auth_http的代碼提交到了交友平台上,因為考慮到產線環境需要高性能的讀寫,才為auth_http服務引入了redis作為中間結果的緩存伺服器。另外,在我這邊的實際環境中,雙因子驗證本身即作為一個獨立的基礎服務提供API調用,所以我在auth_http的實現上也只是簡單的引用了這個服務。根據不同的情況,則可能還需要自己進一步修改其中的業務邏輯和具體內容。
順帶的,我們還能通過auth_http完成頻控,阻斷那些天天開著掃描器在網上拿弱密碼庫到處碰撞的傢伙。
(auth_http中向Nginx返回驗證結果和原始密碼)
實際測試
編譯一下修改的Nginx,這裡要注意,編譯時至少需要指定以下兩個參數,
否則,在載入mail模塊的時候會報告錯誤:
–with-mail –with-mail_ssl_module
這裡提供一個openresty的編譯配置,供大家參考:
編譯需要一點時間,完成之後啟動Nginx:
/opt/openresty/nginx/sbin/nginx
我們測試一下效果。
(正常情況)
(未驗證就想來一發的情況)
(雙因子驗證失敗情況)
(原始密碼錯誤情況)
(觸發頻控被拒絕的情況)
在實際測試過程中,仍然發現了一些缺陷。
其中最為操蛋的是目前還無法支持Exchange協議,這樣一來將導致通訊錄和日曆功能變得不可用。
參考其他廠的解決方案時,發現他們將日曆、通訊錄服務獨立出來,做成一個在線的iCalendar服務讓郵件客戶端調用。
iCalendar
本身可以使用HTTP進行分發(例如使用世界上最好的語言PHP寫的DAViCal
),所以這個問題其實也能解決。突然間,我面對著伺服器上新增的四個服務(Mail代理、auth_http、Redis、iCal),竟然沒有一點欣慰的感覺。為了消滅一個潛在的安全風險,不得不引入多個額外的環節,這麼做是不是在和自己最早的願望背道而馳呢?
*本文原創作者:idapro,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
※看個視頻也被黑?載入字幕文件觸發播放器漏洞實現系統入侵
※看我如何發現Twitter任意賬戶發送推文漏洞並獲得7560美元賞金
※想擁有ShadowBrokers的0day工具嗎?現在加入訂閱計劃,每月僅需21000美元
※分享一種可關閉大多數殺軟的技術
※Malwaresearch:在Openmalware.org上查找惡意軟體的命令行工具
TAG:FreeBuf |
※Windows的Linux 子系統現已支持「複製/粘貼」操作
※Xbox One已確認將支持FreeSync 新電視也要有遊戲模式
※蘋果HomePod支持無損格式:但iTunes卻不支持
※Twitter拓展身份驗證方式:支持通過U盾登錄
※親兒子也沒用 Android P不支持Nexus
※微軟Windows Phone系統怎麼樣 又一款應用不再支持
※Xbox One系統未來將支持FreeSync技術
※Spotify 現已支持Linux snap 通用套件
※難過!HomePod僅支持iPhone設備,Android手機無法兼容
※微軟改口:Xbox One支持VR這事黃了
※微軟宣布Xbox One X以及S主機將支持Dolby Vision功能
※蘋果將停止支持iTunes LP格式,iTunes要被放棄
※蘋果將停止支持 iTunes LP 格式,iTunes 可能也會被砍
※Chrome操作系統終端應用程序可能將支持linux
※微軟量子開發套件更新 現已支持macOS和Linux
※SharePoint更新:引入AI功能 支持混合現實體驗
※蘋果將停止支持 iTunes LP格式 iTunes可能也會被砍
※如果不用Windows,那用什麼操作系統支持國產
※輔助增強工具Office Tool Plus現已支持製作鏡像文件
※新版 iWork 套件已支持Apple Pencil操作