我們首次建模完成之后,迫不及待地讓同事幫忙把數據提取出來,進行人工審核評估,卻發現結果中有很多很多保留IP,心里哇涼哇涼的。每次和客戶對接,我都花很長的時間跟對方的技術人員解釋如何正確地獲取來源IP地址,但是每家公司的情況都有所差別,沒有一個標準方法。也有一些公司,由于相關的代碼已經存在了很久,沒有人維護,而業務系統的架構已經變更了多次,原有的代碼,獲取出來的IP都是錯的。
想象一下,每天都有人在問你:127.0.0.1這個IP是啥?這個IP怎么發了那么多請求?這是不是個基站?還是服務器IP?難不成是代理?還是我們被攻擊了?誒你說話啊?
關于保留IP
下面是從維基百科上摘錄的保留IP地址段,共計16個(最后兩個段一般會合并,也可以認為是15個)。
原文地址:https://en.wikipedia.org/wiki/Reserved_IP_addresses
保留地址段
地址起始
IP地址數量
用途
0.0.0.0/8
0.0.0.0 – 0.255.255.255
16,777,216
軟件
10.0.0.0/8
10.0.0.0 – 10.255.255.255
16,777,216
內網
100.64.0.0/10
100.64.0.0 – 100.127.255.255
4,194,304
內網
127.0.0.0/8
127.0.0.0 – 127.255.255.255
16,777,216
主機(本機)
169.254.0.0/16
169.254.0.0 – 169.254.255.255
65,536
本地子網(DHCP Failed)
172.16.0.0/12
172.16.0.0 – 172.31.255.255
1,048,576
內網
192.0.0.0/24
192.0.0.0 – 192.0.0.255
256
內網
192.0.2.0/24
192.0.2.0 – 192.0.2.255
256
“TEST-NET”
192.88.99.0/24
192.88.99.0 – 192.88.99.255
256
6to4 anycast
192.168.0.0/16
192.168.0.0 – 192.168.255.255
65,536
內網
198.18.0.0/15
198.18.0.0 – 198.19.255.255
131,072
內網
198.51.100.0/24
198.51.100.0 – 198.51.100.255
256
“TEST-NET-2″
203.0.113.0/24
203.0.113.0 – 203.0.113.255
256
“TEST-NET-3″
224.0.0.0/4
224.0.0.0 – 239.255.255.255
268,435,455
預留
240.0.0.0/4
240.0.0.0 – 255.255.255.254
268,435,455
預留
255.255.255.255/32
255.255.255.255
1
廣播
我經常拿這個問題去刁難人,能說出三個段的人,至少是具備網絡基礎知識的,說出5個以上的,一般我會請他喝酒。
連保留IP是啥都不知道的,我就得嘗試用另外一種方式去跟他解釋這個問題了。
保留IP可以說是TCP/IP協議的約定吧,每一個段都有相應的使用說明,都有與之對應的RFC文檔。
比如,中國境內,移動設備在 4G 環境下獲取到的內網IP,一般是 10.0.0.0/8 或者 100.64.0.0/10 的。
非物理隔離的網絡系統,一般會是用 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 內劃分內網地址,比較常見。
據@高春輝說,除了這些之外,還有一些很小的保留 IP 段,如果不詳細去看完整的 whois 數據,可能都不會發現。
聊聊XFF
X-Forwarded-For(XFF)是用來識別通過HTTP代理或負載均衡方式連接到Web服務器的客戶端最原始的IP地址的HTTP請求頭字段。 Squid緩存代理服務器的開發人員最早引入了這一HTTP頭字段,并由IETF在HTTP頭字段標準化草案中正式提出。
XFF的工作機制是,每經過一層代理,由代理服務器,把tcp報文中的Source IP,添加到XFF的末尾,多個IP以逗號分隔。這里說的代理是廣義的,包括負載均衡(比如阿里云SLB),反向代理(比如Nginx),緩存服務器(比如Squid)。
一方面,XFF提供了向后端業務系統傳遞用戶IP的機制,后端業務系統,可以通過XFF感知到訪問者的真實IP。
另一方面,XFF非常易于偽造。很多瀏覽器插件,可以隨機填充XFF字段,如果沒有一套正確的機制來處理XFF字段,而盲目地提取XFF中第一個IP作為訪問者的IP,就一定會出問題。
前面提到了,來源IP是保留IP的情況,其實大多數是由于業務系統直接以TCP報文中的remote address作為來源IP使用了。而這個IP,一般是企業自己的反向代理服務器。
除此之外,XFF偽造的過程中,IP地址是隨機生成的,可能會出線保留IP,非法IP,有少數情況可能會出現“未啟用IP”,也就是說這個IP已經分配給特定的運營商,但是運營商還沒有添加這個IP的路由,這個IP無法被外界訪問,也不會訪問任何人。
這些IP是動態變化的,據老高說,只有分析BGP數據的時候,才能看到哪些IP是沒有被啟用的。
業務系統獲取來源IP的正確姿勢
下面是一個簡單的示意圖,簡單地把整個訪問鏈路劃分成可信區域和不可信區域。
可信區域,就是平臺自己,或者友商建立的系統,可以保證從這些系統中獲取并傳遞的數據是真實的、可信的。
獲取來源IP的正確方式,是提取并記錄本次請求首次進入可信區域時的remote address。不論這個IP是不是代理。

XFF偽造的情況其實非常普遍,也陸續地出現了一些替代方案,我司目前使用的,是設置一個專用的字段來傳遞這個IP,不會和XFF相覆蓋。
此外,某些CDN服務商,會有自己定制化的Header字段,情況比較多,建議結合具體的情況來決定如何獲取用戶的來源IP。
比如,之前遇到一個客戶,使用了阿里云的SLB負載均衡,SLB會給每一個請求都加上X-Forwarded-For字段,他們自己的反向代理又加一次。那么其實只要獲取XFF中倒數第三個IP,作為來源IP即可。
一種參考方式如下:
在反向代理(Nginx)上配置,增加Real-IP字段:
location /{ ... proxy_set_header Real-IP $remote_addr; ...}
業務系統中,獲取來源IP的代碼如下(Java示例):@SuppressWarnings("unchecked")
public static ClientIps getClientIpAddr(HttpServletRequest request) {
// 獲取真實ip
String ip = request.getHeader("real-ip");
if (StringUtils.isBlank(ip) || ("unknown".equalsIgnoreCase(ip.trim()))) {
ip = request.getHeader("remote-host");
}
if (StringUtils.isBlank(ip) || ("unknown".equalsIgnoreCase(ip.trim()))) {
ip = request.getRemoteAddr();
}
ClientIps clientIps = new ClientIps();
clientIps.setTrueIp(StringUtils.trimToEmpty(ip));
// 獲取代理ip
ip = request.getHeader("x-forwarded-for");
StringBuilder proxyIps = new StringBuilder();
if (StringUtils.isNotBlank(ip) && (StringUtils.contains(ip, ","))) {
String temp = StringUtils.substringBeforeLast(ip, ",");
if (StringUtils.isNotBlank(temp)) {
proxyIps.append("x-forwarded-for:");
proxyIps.append(temp);
proxyIps.append("\n");
}
}
這個問題實在是簡單到爆炸,懂技術的同學看到,肯定會噴我,居然寫這種沒水平的文章。
但是呢,作為一個數據分析師,看著每天系統里辣么多保留IP,非法IP傳進來,真的很憋屈。體諒下咯~
而且,每每看別人說攻擊溯源….我滿腦子想的都是:你連獲取到的IP是不是真的你都不知道,你在追溯個啥?
By the way,歡迎有興趣深入研究IP地址的童鞋一起交流,沒準能帶你跟老高一塊兒喝羊湯。
|