摘要:在有反向代理的情況下,直接使用獲取到的地址是所在服務(wù)器的地址,而不是客戶(hù)端的。如何防范方法一在直接對(duì)外的反向代理服務(wù)器上配置如果有多層代理,內(nèi)層的配置在最外層即直接對(duì)外提供服務(wù)的使用代替上面的,可以防止偽造。
問(wèn)題背景
在Web應(yīng)用開(kāi)發(fā)中,經(jīng)常會(huì)需要獲取客戶(hù)端IP地址。一個(gè)典型的例子就是投票系統(tǒng),為了防止刷票,需要限制每個(gè)IP地址只能投票一次。
如何獲取客戶(hù)端IP在Java中,獲取客戶(hù)端IP最直接的方式就是使用request.getRemoteAddr()。這種方式能獲取到連接服務(wù)器的客戶(hù)端IP,在中間沒(méi)有代理的情況下,的確是最簡(jiǎn)單有效的方式。但是目前互聯(lián)網(wǎng)Web應(yīng)用很少會(huì)將應(yīng)用服務(wù)器直接對(duì)外提供服務(wù),一般都會(huì)有一層Nginx做反向代理和負(fù)載均衡,有的甚至可能有多層代理。在有反向代理的情況下,直接使用request.getRemoteAddr()獲取到的IP地址是Nginx所在服務(wù)器的IP地址,而不是客戶(hù)端的IP。
HTTP協(xié)議是基于TCP協(xié)議的,由于request.getRemoteAddr()默認(rèn)獲取到的是TCP層直接連接的客戶(hù)端的IP,對(duì)于Web應(yīng)用服務(wù)器來(lái)說(shuō)直接連接它的客戶(hù)端實(shí)際上是Nginx,也就是TCP層是拿不到真實(shí)客戶(hù)端的IP。
為了解決上面的問(wèn)題,很多HTTP代理會(huì)在HTTP協(xié)議頭中添加X-Forwarded-For頭,用來(lái)追蹤請(qǐng)求的來(lái)源。X-Forwarded-For的格式如下:
X-Forwarded-For: client1, proxy1, proxy2
X-Forwarded-For包含多個(gè)IP地址,每個(gè)值通過(guò)逗號(hào)+空格分開(kāi),最左邊(client1)是最原始客戶(hù)端的IP地址,中間如果有多層代理,每一層代理會(huì)將連接它的客戶(hù)端IP追加在X-Forwarded-For右邊。
下面就是一種常用的獲取客戶(hù)端真實(shí)IP的方法,首先從HTTP頭中獲取X-Forwarded-For,如果X-Forwarded-For頭存在就按逗號(hào)分隔取最左邊第一個(gè)IP地址,不存在直接通過(guò)request.getRemoteAddr()獲取IP地址:
public String getClientIp(HttpServletRequest request) { String xff = request.getHeader("X-Forwarded-For"); if (xff == null) { return request.getRemoteAddr(); } else { return xff.contains(",") ? xff.split(",")[0] : xff; } }
另外,要讓Nginx支持X-Forwarded-For頭,需要配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
$proxy_add_x_forwarded_for會(huì)將和Nginx直接連接的客戶(hù)端IP追加在請(qǐng)求原有X-Forwarded-For值的右邊。
偽造X-Forwarded-For一般的客戶(hù)端(例如瀏覽器)發(fā)送HTTP請(qǐng)求是沒(méi)有X-Forwarded-For頭的,當(dāng)請(qǐng)求到達(dá)第一個(gè)代理服務(wù)器時(shí),代理服務(wù)器會(huì)加上X-Forwarded-For請(qǐng)求頭,并將值設(shè)為客戶(hù)端的IP地址(也就是最左邊第一個(gè)值),后面如果還有多個(gè)代理,會(huì)依次將IP追加到X-Forwarded-For頭最右邊,最終請(qǐng)求到達(dá)Web應(yīng)用服務(wù)器,應(yīng)用通過(guò)獲取X-Forwarded-For頭取左邊第一個(gè)IP即為客戶(hù)端真實(shí)IP。
但是如果客戶(hù)端在發(fā)起請(qǐng)求時(shí),請(qǐng)求頭上帶上一個(gè)偽造的X-Forwarded-For,由于后續(xù)每層代理只會(huì)追加而不會(huì)覆蓋,那么最終到達(dá)應(yīng)用服務(wù)器時(shí),獲取的左邊第一個(gè)IP地址將會(huì)是客戶(hù)端偽造的IP。也就是上面的Java代碼中getClientIp()方法獲取的IP地址很有可能是偽造的IP地址,如果一個(gè)投票系統(tǒng)用這種方式做的IP限制,那么很容易會(huì)被刷票。
偽造X-Forwarded-For頭的方法很簡(jiǎn)單,例如Postman就可以輕松做到:
當(dāng)然你也可以寫(xiě)一段刷票程序或者腳本,每次請(qǐng)求時(shí)添加X-Forwarded-For頭并隨機(jī)生成一個(gè)IP來(lái)實(shí)現(xiàn)刷票的目的。
如何防范 方法一在直接對(duì)外的Nginx反向代理服務(wù)器上配置:
proxy_set_header X-Forwarded-For $remote_addr;
如果有多層Nginx代理,內(nèi)層的Nginx配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
在最外層Nginx(即直接對(duì)外提供服務(wù)的Nginx)使用$remote_addr代替上面的$proxy_add_x_forwarded_for,可以防止偽造X-Forwarded-For。$proxy_add_x_forwarded_for會(huì)在原有X-Forwarded-For上追加IP,這就相當(dāng)于給了偽造X-Forwarded-For的機(jī)會(huì)。而$remote_addr是獲取的是直接TCP連接的客戶(hù)端IP,這個(gè)是無(wú)法偽造的,即使客戶(hù)端偽造也會(huì)被覆蓋掉,而不是追加。
需要注意的是,如果有多層代理,只在直接對(duì)外訪(fǎng)問(wèn)的Nginx上配置X-Forwarded-For為$remote_addr,內(nèi)層的Nginx還是要配置為$proxy_add_x_forwarded_for,不然內(nèi)層的Nginx又會(huì)覆蓋掉客戶(hù)端的真實(shí)IP。
完成以上配置后,業(yè)務(wù)代碼中再通過(guò)上面的getClientIp()方法,獲取X-Forwarded-For最左邊的IP地址即為真實(shí)的客戶(hù)端地址,且客戶(hù)端也無(wú)法偽造。
方法二Tomcat服務(wù)器解決方案:org.apache.catalina.valves.RemoteIpValve
RemoteIpValve可以替換Servlet API中request.getRemoteAddr()方法的實(shí)現(xiàn),讓request.getRemoteAddr()方法從X-Forwarded-For頭中獲取IP地址。也就是在業(yè)務(wù)代碼中不需要再自己實(shí)現(xiàn)類(lèi)似于上面的getClientIp()方法來(lái)從X-Forwarded-For中獲取IP,而是直接使用request.getRemoteAddr()方法。想要使用RemoteIpValve,僅需要在Tomcat配置文件server.xml中Host元素內(nèi)末尾加上:
RemoteIpValve有一套防止偽造X-Forwarded-For的機(jī)制,實(shí)現(xiàn)思路:遍歷X-Forwarded-For頭中的IP地址,和方法一不同的是,不是直接取左邊第一個(gè)IP,而是從右向左遍歷。遍歷時(shí)可以根據(jù)正則表達(dá)式剔除掉內(nèi)網(wǎng)IP和已知的代理服務(wù)器本身的IP(例如192.168開(kāi)頭的IP),那么拿到的第一個(gè)非剔除IP就會(huì)是一個(gè)可信任的客戶(hù)端IP。這種方法的巧妙之處在于,即使偽造X-Forwarded-For,那么請(qǐng)求到達(dá)應(yīng)用服務(wù)器時(shí),偽造的IP也會(huì)在X-Forwarded-For值的左邊,真實(shí)的IP為放到右邊的某個(gè)位置,從右向左遍歷就可以避免取到這些偽造的IP地址。
方法三Node.js 框架 Egg.js 的解決方案:https://eggjs.org/zh-cn/tutor...
Egg.js 可通過(guò)設(shè)置maxProxyCount指定代理層數(shù),然后取X-Forwarded-For頭中從右往左數(shù)第maxProxyCount個(gè)IP即為真實(shí) IP 地址,如果有偽造 IP 地址了必然在最左邊,就會(huì)被忽略掉。
關(guān)注我文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/40422.html
摘要:對(duì)協(xié)議進(jìn)行擴(kuò)展。通過(guò)此項(xiàng)措施,強(qiáng)行將設(shè)置為客戶(hù)端使客戶(hù)端無(wú)法通過(guò)本文所述方式偽造。 在閱讀本文前,大家要有一個(gè)概念,由于TCP需要三次握手連接,在實(shí)現(xiàn)正常的TCP/IP 雙方通信情況下,是無(wú)法偽造來(lái)源 IP 的,也就是說(shuō),在 TCP/IP 協(xié)議中,可以偽造數(shù)據(jù)包來(lái)源 IP ,但這會(huì)讓發(fā)送出去的數(shù)據(jù)包有去無(wú)回,無(wú)法實(shí)現(xiàn)正常的通信。 一些DDoS 攻擊,它們只需要不斷發(fā)送數(shù)據(jù)包,而不需要正...
摘要:對(duì)協(xié)議進(jìn)行擴(kuò)展。通過(guò)此項(xiàng)措施,強(qiáng)行將設(shè)置為客戶(hù)端使客戶(hù)端無(wú)法通過(guò)本文所述方式偽造。 在閱讀本文前,大家要有一個(gè)概念,由于TCP需要三次握手連接,在實(shí)現(xiàn)正常的TCP/IP 雙方通信情況下,是無(wú)法偽造來(lái)源 IP 的,也就是說(shuō),在 TCP/IP 協(xié)議中,可以偽造數(shù)據(jù)包來(lái)源 IP ,但這會(huì)讓發(fā)送出去的數(shù)據(jù)包有去無(wú)回,無(wú)法實(shí)現(xiàn)正常的通信。 一些DDoS 攻擊,它們只需要不斷發(fā)送數(shù)據(jù)包,而不需要正...
摘要:網(wǎng)絡(luò)黑白一書(shū)所抄襲的文章列表這本書(shū)實(shí)在是垃圾,一是因?yàn)樗幕ヂ?lián)網(wǎng)上的文章拼湊而成的,二是因?yàn)槠礈愃教?,連表述都一模一樣,還抄得前言不搭后語(yǔ),三是因?yàn)閮?nèi)容全都是大量的科普,不涉及技術(shù)也沒(méi)有干貨。 《網(wǎng)絡(luò)黑白》一書(shū)所抄襲的文章列表 這本書(shū)實(shí)在是垃圾,一是因?yàn)樗幕ヂ?lián)網(wǎng)上的文章拼湊而成的,二是因?yàn)槠礈愃教?,連表述都一模一樣,還抄得前言不搭后語(yǔ),三是因?yàn)閮?nèi)容全都是大量的科普,不涉及技術(shù)...
閱讀 3475·2023-04-25 18:52
閱讀 2488·2021-11-22 15:31
閱讀 1228·2021-10-22 09:54
閱讀 3016·2021-09-29 09:42
閱讀 611·2021-09-26 09:55
閱讀 915·2021-09-13 10:28
閱讀 1111·2019-08-30 15:56
閱讀 2111·2019-08-30 15:55