HTTP

HTTP 訊息追蹤 (Message Tracing)

(5-2) 代理 (proxy)、隧道 (tunnel) 與 閘道 (gateway)
正常情況下,HTTP 的請求、回應訊息,
將經歷 連接鍊 (chain of connection) 中的連線,
最終抵達 使用者代理 (User Agent, UA) 或 源伺服器 (Origin Server, O)。
 
連接鍊 不一定線性 (linear) 的!
每個 HTTP 參與者,皆可能同時進行多個通訊。
 
 
而獲取 請求鍊狀態 與 User Agent 的身份,則是一重要課題。
 


 

Via

Via 有 通過、路 的意思,
每當訊息「通過」一個 代理 (proxy)閘道 (gateway)
得添加基本資訊 至 Via 表頭欄位
已告知 Client 或 Server,訊息 (message) 是經由哪些 中介 (intermediary) 傳遞:
via
 
格式為:

Via: 上游協定 代理名稱(or IP 位址) [註解資訊]

 
為求簡潔,若 協定 為 HTTP 則僅顯示 協定版本
以常見的 (正/反向) 代理伺服器 — — squid 舉例:

Via: 1.1 JasonProxy (squid/3.5.15)

 
Via 表頭欄位 可用於 追蹤訊息轉發、避免請求迴圈,
或讓下游端點 能根據 上游的協定版本 提供支援的功能。
 
 

多值欄位

多個 中介 添加欄位值時,以「,」為分隔符號:

Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy

 
承上例,若有多個相同協定版本的條目,
且有安全隱私的考量 (e.g., 防火牆後的 主機 IP 位址、port),允許以 假名 (pseudonym) 合併:

Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

 
 

可靠性

由上節可知,via 表頭欄位 僅能做為參考,
並非可靠的中介資訊,
例如,許多 代理 (proxy) 可透過設定,不添加 via 表頭欄位:
squid-disable-via
 


 

X-Forwarded-For

明顯地,上述的 Via 欄位,
對於訊息發起者 — — 使用者代理 (User Agent) 的追蹤,幫助不大。
於是 Squid 開發人員,引進了表頭欄位 — — X-Forwarded-For (XFF)
 
X-Forwarded-For 是 「對…轉發」之意,
可說是最普遍的 客製表頭欄位 之一,其中 "X-" 是指 Custom (客製的)。
 
與 Via 類似,每通過一個 代理 (proxy)閘道 (gateway)
便在 XFF 欄位中新增 來源的 IP 位址 (by TCP/IP 協定)。
(Via 欄位實務上不常置放 IP 資訊)
 
例如:

GET / HTTP/1.1

Host: echo.paw.cloud User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 X-Forwarded-For: 8.8.8.8, 8.8.6.6, 1.2.3.4

 

 
代表 User Agent IP 是 8.8.8.8 !
 
維基百科:

如果沒有 X-Forwarded-For 或者另外一種相似的技術,
所有通過代理伺服器的連線只會顯示代理伺服器的IP位址,而非連線發起的原始IP位址,
這樣的代理伺服器實際上充當了匿名服務提供者的角色,
如果連線的原始IP位址不可得,惡意存取的檢測與預防的難度將大大增加。

 
 

取得 IP 位址

有了 X-Forwarded-For 欄位
便可透過著名的 程式片段 取得 User Agent IP !
(以 PHP 為例)

// Function to get the client IP address
function get_client_ip()
{
    $ip = getenv('HTTP_CLIENT_IP') ?:
        getenv('HTTP_X_FORWARDED_FOR') ?:
            getenv('HTTP_X_FORWARDED') ?:
                getenv('HTTP_FORWARDED_FOR') ?:
                    getenv('HTTP_FORWARDED') ?:
                        getenv('REMOTE_ADDR');
    return $ip;
}

 
原理是先看查看 傳統的 CLIENT_IP 表頭是否有值,
沒有的話則查看 X-Forwarded-For 欄位
還是沒有的話,就直接以 TCP/IP 層,取得連線 IP 位址 (REMOTE_ADDR)。
 
 

可靠性

如此獲取 IP 位址的方式:

一點都不可靠 !
一點都不可靠 !
一點都不可靠 !

 
鑑於資安的重要原則: 不信任
 
要知道偽造表頭是相當容易的,
且偽造者不限於 User Agent、也可能是 中介 (intermediary)
 
較可信的反而只有 TCP/IP 的連線 IP (REMOTE_ADDR),
然而,這只能取得最近的連線者資訊 (可能是 代理 proxy)…。
 
普遍的做法,是以 過濾 (filter) 的方式,排除不信任的部分 (e.g., 127.0.0.1、部分區域 ip),
並將相關欄位 全部儲存,必要之時逐層對 代理 做追蹤。
 
維基百科:

鑑於偽造這一欄位非常容易,應該謹慎使用X-Forwarded-For欄位。
正常情況下 XFF 中最後一個 IP 位址,是最後一個代理伺服器的 IP 位址,
這通常是一個比較可靠的資訊來源。

 


 

DNT

說到 追蹤 (Track),就得提到 DNT (Do Not Track) 表頭欄位
顧名思義「不要追蹤」😂,使用方式非常簡單,設值 0 或 1,
以設置成 1 居多 (啟用 不要追蹤)。
 
用於告知廣告網與網站和應用程式,您拒絕受到行為廣告等機制的追蹤,
遵守該規則的網站,便不會追蹤使用者的訊息,以提供更精卻的廣告。
 
類似的還有 TK 表頭欄位 (草案) :

Tk: N

 
詳情請見 Firefox DNT,與 MDN TK
 
 
另外,也可透過著名的 Disconnect.me 提供的封鎖清單,
防止惡意網站的追蹤、視覺化消除頁面追蹤廣告,以大幅增加讀取速度 !
 

Photo by Disconnect.
 
 
這些技術也時常應用於 瀏覽器的「隱私視窗/模式」:

 


 

Referer

Referer,是史上最智障的表頭欄位 😂。
 
因為正確的拼法,應該是 Referrer (參照/參考),
這源於早期 HTTP 規範的拼寫錯誤,礙於相容性,將錯就錯了…😐。
 
 

無關鍵字

Referer 表頭欄位 簡單說就是:

你從哪個網站來的

 
例如,從 Google 搜尋進入本站:

GET / HTTP/1.1

Host: echo.paw.cloud user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 referer: https://www.google.com.tw/ dnt: 1

 

 
需注意的是:

你無法以 Referer 欄位,應用於關鍵字優化。

 
大部分搜尋引擎,皆因安全性考量,取消了 Referer 中的關鍵字。
 
Referer 欄位 允許 Server 生成與其他資源的反向鏈接 (上一頁),
以進行簡單的分析、記錄、優化的快取等,或用於維護過期或錯誤的連結。
 
 

跨站請求偽造 (CSRF)

跨站請求偽造 (CSRF) 是一種惡意攻擊,
能在使用者 不知情的狀況下,送出請求訊息,
並誤導 Server 為「使用者本人」發出。 (詳見 維基)
 
例如,使用者 Sandra 👱🏻‍♀️ 登入 example.com 後,
可透過該站的連結,購買衣服、或任意商品,
使用者代理 (e.g.,瀏覽器) 可能自動 送出授權憑證 (e.g., cookie、token),確保使用者已登入 !

<a href="/buy?product=clothes">購買</a>

 
而攻擊者可在 另一個頁面 ,放置該連結 :

<img src="https://example.com/buy?product=clothes">

 
Sandra 👱🏻‍♀️ 一旦進入該頁面,img 標籤將送出請求,
且瀏覽器也 貼心地送出了憑證,Server 認證無誤後便下單,
Sandra 就在 不知不覺中 買了好多衣服 😲,
更可怕的是,Sandra 取貨時還不確定是否自己買的 😂。
 

跨站請求偽造 (CSRF) 根本無需竊取帳密,
就能送出惡意的「偽造」請求 😱。

 
某些 Server 則利用 Referer 欄位,做為限制 跨站請求偽造 (CSRF) 的方法,
或 拒絕來自其他站點 (深度連結)。
 
因為,若是 CSRF 攻擊發送的請求,Referer 欄位 會包含 惡意站點的網址
而非原站 URI (範例的 example.com) ,此時 Server 便能識別出惡意的請求。
 
 

CSRF Token

然而,並非所有請求都包含 Referer 欄位
例如,使用者直接在網址列中輸入網址、選取書籤…,
此時 使用者代理 (User Agent) 不會發送 Referer 欄位、或發送欄位值 "about:blank"
 
並且,如 X-Forwarded-For 節所述: 偽造表頭相當容易
 
因此,較佳的做法是使用 CSRF Token (以攻擊者未知的資訊,進行額外驗證),
例如 PHP 框架 — Laravel 的 CSRF Token :

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

 
或 M$ ASP.NET MVC 框架 的 Anti-Forgery Tokens :

@using (Html.BeginForm())
{
    // 產生隱藏的表單欄位 (防偽語彙基元),
    // 表單送出時會驗證這個欄位。
    @Html.AntiForgeryToken()
}

 
更多表單用法,詳見 (4-2) GET vs. POST
 


 

TRACE

最後介紹的是 TRACE 請求方法 !
(Trace vs. Track 含義的不同,可參閱 此篇)
 

沿著 目標資源路徑 執行 訊息 loop-back 測試
Perform a message loop-back test along the path to the target resource.

 
顧名思義,TRACE 主要用於測試,是類似 回聲 (Echo) 的概念,
請求的 最終接收者 (注意此處),必須將收到的請求訊息以 "message/http" 的媒體類型,傳回至 User Agent。
 
請求訊息範例:

TRACE https://example.com/?id=1 HTTP/1.1

Host: example.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 Max-Forwards: 4

 

 
回應訊息範例:

HTTP/1.1 200 OK

Content-Type: message/http Date: Wed, 14 Jun 2017 17:20:15 GMT Server: nginx/1.11.8

 

TRACE /?id=1 Host: example.com User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:53.0) Gecko/20100101 Firefox/53.0 Max-Forwards: 1 Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

 
 
哪泥 !? 為何回應酬載 與 請求訊息 不同 !?
 

因為存在 代理 (Proxy)。

 
從 請求訊息的 請求目標 為 絕對形式 (https://example.com/?id=1),
可合理懷疑 User Agent 配置了 代理伺服器 (proxy)
且收到擁有 3 個值的 Via 欄位,可猜測 連接鏈 至少 存在 3 個以上的中介 (可能合併)。
 
 

Max-Forwards (轉發上限)

其中,一有趣的表頭欄位: Max-Forwards
從字面翻譯不難理解,是 (訊息) 最大的轉發次數
這也是為何,範例中,發送請求時 值為 4,回來卻變成 1 了。
 
每個接收到 TRACEOPTIONS 請求訊息的 中介 (intermediary),
必須再轉發訊息之前,檢查 並 更新 (減一) Max-Forwards 欄位
 
如果接收到的值為零 (0),則中介不得轉發該請求,
並且,中介需做為 最終接收者 發送回應,
這也是為何上方是使用 「最終接收者」而非「源伺服器 (Origin Server)」。
 
Max-Forwards 主要用於 TRACE 與 OPTIONS 請求,
允許 Client 限制請求鏈的長度,這對測試無窮迴圈的 代理鏈 相當實用,
若使用於其他請求方法,則 可能 被 中介 忽略
 
 

屬性 (property)

安全性: Yes
冪等性: Yes
可快取性: No
 
2014 年,HTTP/1.1 修訂中提及:

Client 不得在包含敏感資料的 TRACE 請求中,生成表頭欄位。

 
例如,使用者代理 (User Agent) 在 TRACE 請求中,
發送 用戶憑證 或 狀態資料 (e.g., cookies) 是愚不可及的,
且 (訊息) 最終接收者,應在生成回應時,
排除掉任何包含敏感資料的部分。
 
然而,一切都馬後炮了,
過去十幾年,許多 使用者代理 不良地實作了 TRACE 方法,
使 TRACE 已成為 不安全 的象徵,直至今日。
相同的功能,可使用簡易的 Echo Server 與 OPTIONS 方法取代。
 
 

作者: 鄭中勝
喜愛音樂,但不知為何總在打程式 ? 期許能重新審視、整理自身所學,幫助有需要的人。

發表迴響