引言
起因是某個同事接到了領導安排下來的一個需求,要在一個Web應用(Java+Tomcat)中,記錄用戶登錄時的IP地址和MAC地址,用于安全審計,于是咨詢我如何實現(xiàn)。
第一反應是,這個需求本身是不成立的,根據(jù)以往的了解,MAC地址應該是過不了路由器的才對。
以往做開發(fā),都是用engineer的思維:先動手做,遇到問題再解決問題。但這個需求,應當用scientist的思維去思考:首先確定能不能做,然后才是怎么做。
翻查了一些資料,想來證實"為什么WEB服務器,可以獲取到客戶端的IP地址,但獲取不到MAC地址",看著看著才發(fā)現(xiàn),這是個挺大的命題,夠?qū)懸黄狟LOG了。
PS:由于個人對這塊內(nèi)容了解的不夠徹底,本文很可能會有謬誤,請讀者先不要太當真,另外希望平臺組的同事給予指證。
先說結(jié)論
我所認為的結(jié)論應該是這樣的:
- 獲取遠程主機的IP地址是可行的(先不討論使用代理的情況)
- 本地網(wǎng)絡下,當我們已知一個IPv4地址后,可以通過ARP請求,獲取對應的MAC地址。換句話說,MAC地址,只在本地網(wǎng)絡下才有意義。
- 但我們無法透過路由器,獲取其它網(wǎng)絡下的IPv4節(jié)點的MAC地址。
下面一步步解釋一下。
HTTP
先從HTTP說起。
HTTP是一個應用層的協(xié)議,它建立在TCP協(xié)議之上。
HTTP請求就是用來發(fā)送一段文本。關于這段文本如何組織,第一行寫什么,第二行寫什么,哪里加一個空行,就是HTTP協(xié)議所要規(guī)范的內(nèi)容。
舉個直接的例子,下面是一個簡單的HTTP GET請求,有興趣可以用telnet模擬一下。
GET http://www.fiddler2.com/UpdateCheck.aspx?isBeta=False HTTP/1.1
User-Agent: Fiddler/4.6.3.50306 (.NET 4.0; WinNT 5.1.2600 SP3; zh-CN; 2xx86; Auto Update; Full Instance; Extensions: Geoedge, AutoSaveExt, HostsFile, SAZClipboardFactory, EventLog, SimpleFilter, Timeline, APITesting)
Pragma: no-cache
Host: www.fiddler2.com
Accept-Language: zh-CN
Referer: http://fiddler2.com/client/4.6.3.50306
Accept-Encoding: gzip, deflate
Connection: close
我們可以看到,HTTP的這段請求中,完全找不到客戶端的MAC地址,甚至連IP地址都沒有描述。
那IP地址是從哪里取到的呢?接下來我們再深入一點,看下一個內(nèi)容:Socket
Socket
HTTP的客戶端和服務端,是通過Socket進行連接的。
Socket是什么呢?Socket是對OSI模型第4層-傳輸層中的TCP/IP協(xié)議的封裝。Socket本身并不是協(xié)議,而是一個調(diào)用接口(API)。Socket和TCP/IP協(xié)議沒有必然的聯(lián)系。但通過Socket,我們才能使用TCP/IP協(xié)議。應用層不必了解TCP/IP協(xié)議細節(jié),直接通過對Socket接口函數(shù)的調(diào)用完成數(shù)據(jù)在IP網(wǎng)絡的傳輸。
Socket包含了網(wǎng)絡通信必須的五種信息:連接使用的協(xié)議,本地主機的IP地址,本地進程的協(xié)議端口,遠地主機的IP地址,遠地進程的協(xié)議端口。
所以,因為有了Socket,客戶端和服務端完全不需要了解底層細節(jié),直接通過調(diào)用Socket來實現(xiàn)就可以了。
這也就是為什么服務器端可以獲取到客戶端的IP地址的原因,因為Socket中包含了遠地主機的IP地址。(當然,通過代理服務器進行訪問的除外,這種要依靠HTTP協(xié)議的X-Forwarded-For頭來確認IP,不在本次的討論范圍中)
那為什么無法獲取到客戶端的MAC地址呢?很簡單,同理,因為Socket中無法取到MAC地址。。。
如果繼續(xù)發(fā)問,為什么Socket中都既然都包含IP地址了,為什么偏偏不包含MAC地址信息呢?看來我們還要更深入一點,看一下OSI模型吧。
OSI七層模型
首先祭出這張經(jīng)典的OSI七層模型圖,計算機網(wǎng)絡的基石,請先盯著看一會兒,認真復習一下

這里還有一張OSI七層模型與TCP/IP四層模型的對照圖

為了方便理解,再放上一張更直觀的,每一層對應的數(shù)據(jù)型式和主要協(xié)議的示意圖

通過上圖大體可以知道:
- MAC地址是記錄在第2層-數(shù)據(jù)鏈路層的
- IP地址是記錄在第3層-網(wǎng)絡層的
- 端口號(TCP/UDP)是位于于第4層-傳輸層的
- HTTP請求報文是記錄在第7層-應用層的
當打開一個URL時,究竟發(fā)生了什么
下面舉個栗子,當我們在瀏覽器中打開一個鏈接后,看看OSI各層倒底發(fā)生了什么:
這里撇開DNS解析之類東西,只說一下HTTP報文的發(fā)送
1、發(fā)送端
首先來看一下發(fā)送端(瀏覽器所在的主機)。參照第一張OSI模型圖,按照從上向下的順序來看。應用層數(shù)據(jù)其實只有那么幾行文本,然后往下,每過一層,都要被加上首部/尾部。這個過程就像是一層一層的穿衣服。
HTTP請求文本:

最后到了數(shù)據(jù)鏈路層之后,數(shù)據(jù)就變成了這個肥肥的樣子,最后轉(zhuǎn)換成0和1的電信號發(fā)出:

下面看看,現(xiàn)實中,每一層都做了些什么(現(xiàn)實中的分層和OSI模型還是有區(qū)別的):
第7,6,5層,也就是OSI中的應用層,表示層,會話層(也是TCP/IP分層中的應用層),它創(chuàng)建了一個HTTP請求(例如 GET / HTTP/1.1),并交給下一層。一個普通的HTTP GET請求就是幾行純文本。
處理這三層,是瀏覽器和WEB服務器所要做的工作,瀏覽器發(fā)出請求,WEB服務器做出響應。第4,3層,也就是傳輸層/網(wǎng)絡層。TCP/IP棧將上層的數(shù)據(jù)分成包(packets),并將它送往下一層-數(shù)據(jù)鏈路層。
IP地址、端口號記錄在這兩層中。
處理這兩層,是操作系統(tǒng)要做的工作,操作系統(tǒng)將這兩層封裝為了Socket,方便調(diào)用。第2層,數(shù)據(jù)鏈路層,將包(package)封裝成幀(frame),并將它送到下一層物理層。
MAC地址記錄在這一層中。
這一層的工作,交由網(wǎng)卡來處理。第1層,物理層,使用電信號來傳送0和1。
最后這一層就是傳輸介質(zhì)的工作了,例如雙絞線、光纖、同軸電纜。
2、數(shù)據(jù)流轉(zhuǎn)
數(shù)據(jù)發(fā)出去后,再看一下數(shù)據(jù)在網(wǎng)絡上的流轉(zhuǎn)。
數(shù)據(jù)一般要經(jīng)過交換機、路由器等網(wǎng)絡設備,層層轉(zhuǎn)發(fā),這些設備所做的事情就像是: 脫掉一件或幾件衣服,做一些修修補補,然后再重新穿回去。
- 這里先以L2交換機為例看一下,因為L2交換機會認別到幀這一層,記錄/學習MAC地址,并將幀發(fā)送到目的地。
通過上面這張圖,我們就可以理解,MAC地址在本地網(wǎng)絡下的重要作用了。也理解了,本地網(wǎng)絡下,是可以查出每個節(jié)點的MAC地址的。
- 下面,再來看一下路由器。當一個LAN希望連接到另一個LAN時,就必須使用路由器設備了。當然,可以通過構建超大型的LAN,來避免使用路由器,但這時,交換機就需要管理大量的MAC地址,同時進行大量的廣播通信,設備的負擔就會相當大。路由器會進行路由選擇,讓數(shù)據(jù)達到下一跳。
經(jīng)過路由器后,為了能到達下一跳,數(shù)據(jù)鏈路層中的MAC地址就被篡改了,下面這張圖很能說明問題:

3、接收端
最后看一下接收端(WEB服務器所在的主機)。參照第一張OSI模型圖,按照從下至上的順序來看,它要做的事情是: 將衣服一件一件全部脫掉 ,最后WEB服務器就取到了最初的應用層數(shù)據(jù)。
4、結(jié)論
所以,當一個以太網(wǎng)幀到達目的主機后,其中的MAC地址早已經(jīng)不是原來客戶端的MAC了,操作系統(tǒng)的Socket自然也無法獲取原始的MAC地址了。
可不可以通過其它方式獲取MAC地址
上面已經(jīng)證明了,WEB服務端,是無法獲取客戶端的MAC地址的。
那么,能不能通過一些trick來繞道實現(xiàn)呢?
想了想,大概可以有如下的思路:
- 通過JavaScrip獲取客戶端的某塊網(wǎng)卡的MAC地址,最好還要同時獲取路由表,挑選出走默認路由的那塊網(wǎng)卡。
- 將MAC地址,偷偷寫入應用層的HTTP協(xié)議的Header中(類似Content-Type和User-Agent)
- 服務端直接讀Header,就可以取到Mac地址了
那么這個思路可不可行呢?
- 如果客戶端是瀏覽器的話,首先第一步就會被打臉,我想也沒有哪個瀏覽器會允許這種不安全的操作的。但如果硬要通過IE下的ActiveX來取的話,貌似也不是不可以。。。
- 但如果客戶端是APP的話,通過WebView+JS Bridge方式調(diào)用私有的API,應該也是可行的。。。
記錄MAC地址的意義
最后的最后,不禁思考,獲取MAC的意義在哪里呢?
如果單純是為了取證和審計,我想意義是不大的,甚至不如直接記錄IP地址。
因為:
- 對于普通的正常用戶而言,通過IP地址,是可以直接定位操作人的,即使IP是動態(tài)的。
- 對于初級以上Hacker來說,偽造MAC地址和偽造IP地址一樣簡單,甚至更簡單。
所以,一般的安全管控要求下,還是只記錄IP吧。
參考資料
- https://www.amazon.cn/圖解網(wǎng)絡硬件-日-三輪賢一/dp/B01531731E/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1488177601&sr=1-1&keywords=圖解網(wǎng)絡硬件
- https://www.amazon.cn/圖解網(wǎng)絡硬件-日-三輪賢一/dp/B01531731E/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1488177601&sr=1-1&keywords=圖解網(wǎng)絡硬件
- https://www.amazon.cn/HTTP權威指南-美-David-Gourley-Brian-Totty-Marjorie-Sayer-Sailu-Reddy-Aushu-Aggarwal/dp/B00M2DKYRC/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1488178503&sr=1-1&keywords=HTTP權威指南
- http://superuser.com/questions/801788/role-of-osi-layers-when-we-open-a-url-in-browser

