<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 配置 ~~~ proxy_set_header Host $host; #只要用戶在瀏覽器訪問的域名綁定了VIP,VIP下面有RS;則使用$host;host是訪問URL中的域名和端口 www.taobao.com:80 proxy_set_header X-Real-IP $remote_addr; #把源IP【$remote_adr,建立HTTP連接header里的信息】賦值給X-Real-IP,這樣在代碼 $X-Real-IP獲取源IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#在 nginx 作為代理服務器,設置的IP列表,會把經過的機器IP,代理機器IP都記錄下來,用【,】隔開,代碼中用 echo $x-forwarded-for | awl -F, '{print $1}' 作為源IP ~~~ # 背景 通過名字就知道,X-Forwarded-For 是一個 HTTP 擴展頭部。HTTP/1.1(RFC 2616)協議并沒有對它的定義,它最開始是由 Squid 這個緩存代理軟件引入,用來表示 HTTP 請求端真實 IP。如今它已經成為事實上的標準,被各大 HTTP 代理、負載均衡等轉發服務廣泛使用,并被寫入[RFC 7239](http://tools.ietf.org/html/rfc7239)(Forwarded HTTP Extension)標準之中。 X-Forwarded-For 請求頭格式非常簡單,就這樣: ~~~ X-Forwarded-For: client, proxy1, proxy2 ~~~ 可以看到,XFF 的內容由「英文逗號 + 空格」隔開的多個部分組成,最開始的是離服務端最遠的設備 IP,然后是每一級代理設備的 IP。 <br> 如果一個 HTTP 請求到達服務器之前,經過了三個代理 Proxy1、Proxy2、Proxy3,IP 分別為 IP1、IP2、IP3,用戶真實 IP 為 IP0,那么按照 XFF 標準,服務端最終會收到以下信息: ~~~ X-Forwarded-For: IP0, IP1, IP2 ~~~ Proxy3 直連服務器,它會給 XFF 追加 IP2,表示它是在幫 Proxy2 轉發請求。列表中并沒有 IP3,IP3 可以在服務端通過 Remote Address 字段獲得。我們知道 HTTP 連接基于 TCP 連接,HTTP 協議中沒有 IP 的概念,Remote Address 來自 TCP 連接,表示與服務端建立 TCP 連接的設備 IP,在這個例子里就是 IP3。 <br> Remote Address 無法偽造,因為建立 TCP 連接需要三次握手,如果偽造了源 IP,無法建立 TCP 連接,更不會有后面的 HTTP 請求。不同語言獲取 Remote Address 的方式不一樣,例如 php 是`$_SERVER["REMOTE_ADDR"]`,Node.js 是`req.connection.remoteAddress`,但原理都一樣。 <br> <br> # 問題 這段代碼會監聽`9009`端口,并在收到 HTTP 請求后,輸出一些信息: ~~~ var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('remoteAddress: ' + req.connection.remoteAddress + '\n'); res.write('x-forwarded-for: ' + req.headers['x-forwarded-for'] + '\n'); res.write('x-real-ip: ' + req.headers['x-real-ip'] + '\n'); res.end(); }).listen(9009, '0.0.0.0'); ~~~ 這段代碼除了前面介紹過的 Remote Address 和`X-Forwarded-For`,還有一個`X-Real-IP`,這又是一個自定義頭部字段。`X-Real-IP`通常被 HTTP 代理用來表示與它產生 TCP 連接的設備 IP,這個設備可能是其他代理,也可能是真正的請求端。需要注意的是,`X-Real-IP`目前并不屬于任何標準,代理和 Web 應用之間可以約定用任何自定義頭來傳遞這個信息。 <br> 現在可以用域名 + 端口號直接訪問這個 Node.js 服務,再配一個 Nginx 反向代理: ~~~ server{ listen 80; server_name 127.0.0.1; root /srv/ip-test; index index.html; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:9009/; proxy_redirect off; } } ~~~ Nginx 監聽`80`端口,所以不帶端口就可以訪問 Nginx 轉發過的服務。 <br> 測試直接訪問 Node 服務: ~~~ curl http://192.168.199.217:9009/ remoteAddress: 192.168.199.180 x-forwarded-for: undefined x-real-ip: undefined ~~~ 由于我的電腦直接連接了 Node.js 服務,Remote Address 就是我的 IP。同時我并未指定額外的自定義頭,所以后兩個字段都是 undefined。 <br> 再來訪問 Nginx 轉發過的服務: ~~~ remoteAddress: 127.0.0.1 x-forwarded-for: 192.168.199.180 x-real-ip: 192.168.199.180 ~~~ 通過 Nginx 訪問 Node.js 服務,得到的 Remote Address 實際上是 Nginx 的本地 IP。而前面 Nginx 配置中的這兩行起作用了,為請求額外增加了兩個自定義頭: ~~~ proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ~~~ <br> HTTP 請求頭可以隨意構造,我們通過 curl 的`-H`參數構造`X-Forwarded-For`和`X-Real-IP`,再來測試一把。 直接訪問 Node.js 服務: ~~~ curl http://192.168.199.217:9009/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2' remoteAddress: 192.168.199.217 x-forwarded-for: 1.1.1.1 x-real-ip: 2.2.2.2 ~~~ 對于 Web 應用來說,`X-Forwarded-For`和`X-Real-IP`就是兩個普通的請求頭,自然就不做任何處理原樣輸出了。這說明,對于直連部署方式,除了從 TCP 連接中得到的 Remote Address 之外,請求頭中攜帶的 IP 信息都不能信。 <br> 訪問 Nginx 轉發過的服務: ~~~ curl http://192.168.199.217/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2' remoteAddress: 127.0.0.1 x-forwarded-for: 1.1.1.1, 192.168.199.217 x-real-ip: 192.168.199.217 ~~~ 這一次,Nginx 會在`X-Forwarded-For`后追加我的 IP;并用我的 IP 覆蓋`X-Real-IP`請求頭。這說明,有了 Nginx 的加工,`X-Forwarded-For`最后一節以及`X-Real-IP`整個內容無法構造,可以用于獲取用戶 IP。 <br> 用戶 IP 往往會被使用與跟 Web 安全有關的場景上,例如檢查用戶登錄地區,基于 IP 做訪問頻率控制等等。這種場景下,確保 IP 無法構造更重要。經過前面的測試和分析,對于直接面向用戶部署的 Web 應用,必須使用從 TCP 連接中得到的 Remote Address;對于部署了 Nginx 這樣反向代理的 Web 應用,在正確配置了 Set Header 行為后,可以使用 Nginx 傳過來的`X-Real-IP`或`X-Forwarded-For`最后一節(實際上它們一定等價)。 <br> 那么,Web 應用自身如何判斷請求是直接過來,還是由可控的代理轉發來的呢?在代理轉發時增加額外的請求頭是一個辦法,但是不怎么保險,因為請求頭太容易構造了。如果一定要這么用,這個自定義頭要夠長夠罕見,還要保管好不能泄露出去。 <br> 判斷 Remote Address 是不是本地 IP 也是一種辦法,不過也不完善,因為在 Nginx 所處服務器上訪問,無論直連還是走 Nginx 代理,Remote Address 都是 `127.0.0.1`。這個問題還好通常可以忽略,更麻煩的是,反向代理服務器和實際的 Web 應用不一定部署在同一臺服務器上。所以更合理的做法是收集所有代理服務器 IP 列表,Web 應用拿到 Remote Address 后逐一比對來判斷是以何種方式訪問。 <br> 首先,如果用戶真的是通過代理訪問 Nginx,`X-Forwarded-For`最后一節以及`X-Real-IP`得到的是代理的 IP,安全相關的場景只能用這個,但有些場景如根據 IP 顯示所在地天氣,就需要盡可能獲得用戶真實 IP,這時候`X-Forwarded-For`中第一個 IP 就可以排上用場了。這時候需要注意一個問題,還是拿之前的例子做測試: ~~~ curl http://192.168.199.217/ -H 'X-Forwarded-For: unknown, <>"1.1.1.1' remoteAddress: 127.0.0.1 x-forwarded-for: unknown, <>"1.1.1.1, 192.168.199.217 x-real-ip: 192.168.199.217 ~~~ <br> `X-Forwarded-For`最后一節是 Nginx 追加上去的,但之前部分都來自于 Nginx 收到的請求頭,這部分用戶輸入內容完全不可信。使用時需要格外小心,符合 IP 格式才能使用,不然容易引發 SQL 注入或 XSS 等安全漏洞。 <br> <br> # 結論 1. 直接對外提供服務的 Web 應用,在進行與安全有關的操作時,只能通過 Remote Address 獲取 IP,不能相信任何請求頭; 2. 使用 Nginx 等 Web Server 進行反向代理的 Web 應用,在配置正確的前提下,要用`X-Forwarded-For`最后一節 或`X-Real-IP`來獲取 IP(因為 Remote Address 得到的是 Nginx 所在服務器的內網 IP);同時還應該禁止 Web 應用直接對外提供服務; 3. 在與安全無關的場景,例如通過 IP 顯示所在地天氣,可以從`X-Forwarded-For`靠前的位置獲取 IP,但是需要校驗 IP 格式合法性; <br> PS:網上有些文章建議這樣配置 Nginx,其實并不合理: ~~~ proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; ~~~ 這樣配置之后,安全性確實提高了,但是也導致請求到達 Nginx 之前的所有代理信息都被抹掉,無法為真正使用代理的用戶提供更好的服務。 # 參考資料 [HTTP 請求頭中的 X-Forwarded-For](https://imququ.com/post/x-forwarded-for-header-in-http.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看