Skip to content

網路流量與 HTTP Repeater

這個面板顯示的每一個字串都是硬寫在元件裡的英文。把網站切成繁體中文不會翻譯面板標題、欄位名稱、按鈕文字或空狀態文字——本頁引用的那些標籤,在中英文介面底下看到的都是同一組英文。

Network Traffic 面板

面板工具列左側固定顯示標題 Network Traffic。旁邊是目前的記錄筆數:剛好一筆時寫 1 request,其餘筆數(含零筆)一律寫 N requests

工具列右側是一顆 Clear 按鈕。按下去會清空整份記錄、收合目前展開中的明細,並把編號歸零,所以 Clear 之後記錄到的下一筆是 1,不會接續先前的最大號碼。

還沒有任何記錄時,面板中央顯示 No requests recorded yet

Network 面板是唯一不受就緒閘門管轄的工具面板。執行環境與 Service Worker 還在啟動時,四顆分頁按鈕全都點得動、每個分頁都切得過去——沒有任何一顆分頁按鈕會被停用。被停用的是內容:Browser、Repeater、Code 三個面板收到一個停用旗標,面板內的控制項因此變灰按不動。Network 面板從來不會收到這個旗標,所以照樣完全可以操作——只是還沒有任何流量可看。

欄位

清單固定有五欄,順序如下。

欄位顯示什麼
#序號,從 1 開始,每記錄一筆遞增
Method該請求的 HTTP 方法
URL只有路徑與 query string,協定與主機名稱被去掉。記錄到的網址無法被解析時,原字串原樣顯示
Status回應的狀態碼,帶顏色
Time該請求的耗時,一個整數接上 ms

沒有來源欄,也沒有時間戳欄位。每筆記錄裡確實存了時間戳,但表格不顯示它。

新記錄一律附加到清單最後,所以表格由上到下是由舊到新。

Time 是包住整個 dispatch 前後量測、再四捨五入成整數毫秒。本機回應很快時 0ms 是正常讀數,不代表出了問題。

狀態碼配色

Status 欄只依數值門檻上色。

狀態碼顏色
500 以上紅色
400 到 499橘色
300 到 399黃色
其餘,含 2xx 與 1xx綠色

顏色就只是一條繪製規則。想知道某個狀態碼說明了伺服器如何處理該請求,看知識庫的 HTTP 狀態碼

哪些請求會被記錄

Browser 面板、Repeater 與 Code 編輯器三者的請求都經同一層 tracked dispatch 送出,因此三者都進同一份記錄。沒有任何已開放的面板被排除在外。

表格不會標示某筆記錄由哪個面板發出。來源面板確實有被記下來,但只寫進可匯出的攻擊 session,不會出現在你看到的那一列。要把記錄對回產生它的操作,就用序號與它在擷取順序中的位置。

面板裡呈現的這份記錄住在頁面的記憶體中。它沒有筆數上限也不會被裁切,會一直累積到你按 Clear 為止。它的範圍就是你目前所在的這個題目頁面:換頁、重新整理或切換語系都會讓整份記錄清空——切換語系等於導向另一語系的同一頁網址,整頁重新載入。

每一筆的完整內容(含 header 與請求、回應兩邊的內容)另外會被複製進該題的攻擊 session 並寫入 IndexedDB。那一份副本在重新整理後仍然存在,即使畫面上的清單已經空了,仍可由匯出的 session 讀到。

執行環境沒有 worker 可派送時,該請求不會消失。它會以狀態碼 503 被記錄下來,在清單中顯示為一列紅色。

讀一筆記錄

點一列會在該列正下方展開明細。再點同一列即收合。

明細區有 RequestResponse 兩個子分頁,每次展開都停在 Request

Request 把該筆重組成一封 raw HTTP 訊息:第一行是 方法 /路徑 HTTP/1.1 形式的請求行,接著是 header 行、一個空行,然後是請求內容。

那一段 header 不是 fetch 真的送得上線的那幾個。它是一組模擬 Chrome 的 header——HostConnectionSec-Ch-UaUser-AgentAcceptSec-Fetch-* 這一家族——與程式實際送出的自訂 header 取聯集。不在模擬清單內的自訂 header 會被接在後面。請求帶了 cookie 時,模擬那一群的最後會多一行 Cookie

ResponseHTTP/1.1 <狀態碼> 起頭——本實驗室從來不填狀態文字那一格,這裡與 Repeater 的回應區都一樣——接著是回應 header、一個空行,然後是回應內容。伺服器設的 cookie 會被還原成一般的 Set-Cookie header 行,一個 cookie 一行。這個明細區塊有固定的高度上限,訊息超過時區塊內自行捲動。

子分頁列上方可能出現一塊開頭寫 Runtime diagnostic: 的紅框。只有 PHP 執行環境會發出它,內容是它在組出這份回應時丟掉了什麼——例如附在不能帶主體的狀態碼上的輸出,或題目試圖設定的保留 header。本 repo 現存題目的後端全是 Python,所以目前沒有任何一題產生得出這塊紅框。

Send to Repeater

Send to Repeater 按鈕位於子分頁列的最右端。它只存在於展開後的明細區內,所以要先展開某一列才看得到。

沒有開放 Repeater 分頁的題目根本不會繪製這顆按鈕。它是不存在,不是停用。

  1. 在流量清單中點你要的那一列。
  2. 在展開的明細區裡點 Send to Repeater
  3. 畫面自動切換到 Repeater 分頁。
  4. 該筆的 raw 請求文字會覆蓋 Repeater 編輯區裡原本的內容。

對同一列連按兩次 Send to Repeater,第二次不會有作用。注入的值會與前一次比對,值相同就不觸發覆寫——分頁仍然會切過去,但你改過的內容留著。要拿回未經修改的原始請求,先展開另一列再回來,或還原一個 snapshot。

Repeater 面板

Repeater 以純文字編輯一整封 HTTP 訊息。沒有拆開的方法、網址、header、內容欄位。

控制項行為
Raw HTTP Request 編輯區單一的自由文字編輯區,容納完整訊息:請求行、header 行、一個空行,然後是內容
Send依你寫的內容原樣送出。執行環境或 Service Worker 尚未就緒時會變半透明且按不下去
回應區顯示以 HTTP/1.1 <狀態碼> 起頭的回應——狀態文字那一格永遠是空的——接著 header、一個空行與回應內容
+ Save展開一列,提示 Snapshot name: 要求為即將儲存的 snapshot 命名
Saved Snapshots 側欄列出已存的請求;沒有任何 snapshot 時顯示 No snapshots yet

面板一開啟就已經填好一份 GET / HTTP/1.1 請求,Hostchallenge-<題目 slug>.localhost,後面接著 User-AgentAcceptAccept-LanguageAccept-EncodingConnection 等 header。

實際打出去的網址由 Host header 與請求行裡的路徑組成。把 Host 改成另一題的 slug,請求就送去那一題,而流量記錄裡對應的那一筆也會顯示改後的 Host

請求格式

先寫請求行,之後每行一個 header,然後一個空行,最後是內容。

POST /items HTTP/1.1
Host: challenge-example.localhost
Content-Type: application/x-www-form-urlencoded

name=widget&quantity=2

分隔 header 與內容的就是那個空行。少了它,內容文字會被讀成另一行 header。

在 raw 請求裡寫 Cookie header 是有效的。平台會以另一個 header 名稱把它載過邊界,在後端側還原之後題目才會看到它。

兩種看起來壞掉的情形

第一行是空的。 請求文字的第一行為空時,回應區顯示 Error: invalid request format,而且什麼都不會送出。

按 Send 完全沒反應。 只要第一行非空,這段文字就會被交給瀏覽器的 request 建構子。建構子若拒絕它——GETHEAD 帶了內容(在分隔空行後多打一個換行就會發生),或方法名不是合法 token——這個失敗不會被接住。回應區既不更新也不顯示錯誤訊息,於是 Send 看起來完全沒作用。在斷定題目掛掉之前,先檢查第一行與結尾多出來的換行。

回應區與 Network 面板的差異

Repeater 的回應區直接把瀏覽器暴露的 header 原樣印出來,因此每個 header 名稱都是小寫。伺服器設的 cookie 在這裡不會被還原:它們以單一一個 x-wxlsh-set-cookie header 出現,值是一串以逗號分隔的 base64——那是平台用來把 cookie 載過邊界的載送 header,而 Repeater 在它被拆開之前就印出來了。

同一筆交換在 Network 面板的 Response 分頁裡卻是拆開後的 Set-Cookie 行。因此同一次往返在兩處看起來不一樣;要看正常形式的 cookie 就讀 Network 面板那一份。

Snapshot

  1. 編輯區裡放好你要的請求,點 + Save
  2. 在出現的那一列輸入名稱。Enter 確認,Escape 取消。
  3. 名稱空白或只有空白字元時按下去沒有作用——儲存列會一直開著,直到你輸入真正的名稱或按 Escape

snapshot 只保存請求文字,不會連同回應一起存。

Saved Snapshots 側欄裡的 snapshot 名稱,會把它的內容寫回編輯區,覆蓋目前的文字。滑鼠移到某個 snapshot 上會浮出一顆 ×,按下即刻刪除該筆,沒有確認步驟。

snapshot 存在瀏覽器的 localStorage,key 含題目 slug。關掉分頁重開仍然在,而且某一題存的 snapshot 不會出現在另一題。

走查範例:改一個參數再比對

以下示範這趟往返的操作機制。它不以任何東西為標的,也不對應用程式下任何結論——重點是點哪裡、比對什麼。

  1. 在 Browser 面板做任何一個會發出請求的動作,例如送出表單或點一個帶 query string 的連結。
  2. 開啟 Network 分頁。該請求會以新的一列出現在清單最下方。記下它的 StatusTime
  3. 點那一列。在 Request 子分頁讀那封 raw 訊息,找到你想改動的那個參數。
  4. Send to Repeater。畫面切到 Repeater,該封訊息已經載入。
  5. + Save,為 snapshot 命名後按 Enter。這給你一條回到未修改請求的路。
  6. Raw HTTP Request 編輯區改掉一個參數的值,其餘一律不動。
  7. Send。回應區會填入狀態行、header 與回應內容。
  8. 與原本那一筆比對:狀態碼、header 集合、以及回應內容的長度或內容本身。改一個參數、觀察一個差異,這就是一個完整的工作單位。
  9. 切回 Network 分頁。重送的那個請求以自己的一列在記錄裡,你可以在那裡讀到還原後的 Set-Cookie header 與記下的 Time

要試第二種變化,點側欄裡的 snapshot 還原原始文字,再從步驟 6 重來。

面板行為不如預期時

流量記錄一片空白、Send 按鈕一直停用、第一次載入等很久,這些都有已知的成因。它們連同各自的症狀與處理方式列在疑難排解