0.25 短網址服務的瞬時大流量:爆紅連結、建立路徑的機器人濫用與轉址路徑的攻擊
這篇整理短網址服務遇到瞬時大流量時的處置,前提是 0.24 短網址服務的實作:短碼生成、對照表儲存、轉址快取與點擊紀錄 的實作已經存在:建立路徑與轉址路徑分開、各層快取、非同步的點擊事件與濫用防護。不限於短網址的通用機制(流量來源的辨識、擴展緩衝、負載卸除、准入控制、體積型攻擊)見 9.15 無預警瞬時大流量:流量來源辨識、擴展緩衝、請求優先等級、准入控制、體積型攻擊與退路,這篇只寫它們落在短網址服務上的形狀。
短網址服務的瞬時大流量有三種來源,落在不同的路徑上:一條連結被大量轉發,流量落在轉址路徑上而且集中在單一短碼;機器人大量建立連結,流量落在建立路徑上;有人掃描短碼或攻擊轉址網域,流量落在轉址路徑上而且分散在不存在的短碼或整個入口。三者在 9.15 的分類裡分別是真人需求、應用層的濫用流量,以及應用層的濫用流量或體積型攻擊。
爆紅連結的負載落點:CDN、程序內快取與對照表
一條連結被放進大量轉發的貼文之後,幾小時內的點擊數可能超過整個服務平常一天的量,而且全部打在同一個短碼上。這些點擊預設落在哪一層,由需求定義裡點擊資料的地位決定:點擊分析是不是要對客戶交付的功能(0.23 短網址服務的需求定義:自建或購買、保存期限、目的地修改與濫用處理的規格 的〈點擊資料的地位:付費功能或內部參考,以及對應的規格〉);同一篇〈建立者的範圍與濫用處理〉的撤回時限,則限制了任何一層能保存轉址多久。
不統計點擊、或點擊只是內部參考時,轉址回應可以讓 CDN 保存,爆紅連結的點擊絕大多數由 CDN 直接回應,應用伺服器只在 CDN 的副本到期時收到請求(每個邊緣節點各一次,有 origin shield(CDN 內部集中回源的中間層,見 5.9 邊緣分發與靜態資源(CDN / Origin Protection) 的〈Origin Protection 的設計責任〉)這類中間層時才接近整體一次)。點擊只是內部參考時,點擊事件改由 CDN 的存取紀錄產生,應用伺服器不再發出(0.24 的〈各層快取與撤回所需的時間〉)。CDN 保存轉址時,爆紅幾乎不會影響後端,承受流量的是 CDN 的頻寬,CDN 的流量費用隨點擊數增加。
點擊分析是付費功能時,轉址回應多半帶 Cache-Control: private,CDN 不保存,每一次點擊都送到應用伺服器。0.24 的〈各層快取與撤回所需的時間〉已經處理這條連結成為 hot key 的情形:每台應用伺服器的記憶體裡放一層短 TTL 的程序內快取,讓同一個短碼的讀取分散到所有伺服器,而不是全部打在同一個共用快取節點上。剩下的瓶頸是應用伺服器的數量本身,伺服器數量要靠自動擴展追上;爆紅的上升速度比自動擴展的反應時間快時,差距由擴展緩衝承接,緩衝的算法見 9.15 的〈自動擴展(autoscaling)的反應時間與緩衝〉。
這個預設在爆紅時有一個例外做法:平常不讓 CDN 保存轉址,origin 統計單一短碼的請求速率(計數放在各台應用伺服器共用的計數器裡,或每台依伺服器台數換算門檻),超過門檻時,只對那一個短碼讓 CDN 保存幾秒到幾十秒,點擊事件改由 CDN 的存取紀錄補回;只補回存取紀錄裡由 CDN 直接回應(快取命中)的那幾筆,回源到 origin 的請求 origin 已經發過點擊事件,全部補回會重複計數。最直接的做法是 origin 只在這個短碼的回應裡,把 private 換成 s-maxage=<秒數>:private 會禁止 CDN 保存,留著它 s-maxage 不會生效;max-age 照舊留給瀏覽器,不必改 CDN 設定(兩個指令見 12.2 新鮮度期限的計算:max-age、s-maxage、Expires 與啟發式期限)。開啟之後,origin 看到的這個短碼請求速率會掉到門檻以下,所以關閉的條件不能用 origin 端的速率,否則保存規則會一開一關地擺盪;開啟後給一段固定的保存期,或改看 CDN 存取紀錄算出的總速率再決定關閉。這樣做的前提是 CDN 遵循 origin 的 s-maxage、存取紀錄能在可接受的延遲內送回,而且這套切換在流量來之前就要建好並演練過,當下只是觸發它。
點擊事件管道的積壓與非人為請求
轉址路徑撐住之後,壓力往後移到點擊事件的管道。0.24 把點擊紀錄設計成轉址時只發出一筆事件、由背景消費者彙總,所以爆紅時轉址不會變慢,佇列裡未處理的事件數(queue depth)會上升,消費者處理到的位置落後最新事件越來越多(consumer lag)。客戶在報表上看到的是點擊數晚了幾分鐘到幾小時才更新。
這個積壓要不要處理、處理到什麼程度,看的是點擊報表的時效承諾。點擊分析是付費功能、報表承諾近即時時,消費者要能跟著事件量擴展,佇列的保存時間要長過最長的預期積壓,否則事件在被處理之前就過期遺失;點擊只是內部參考時,積壓只是報表晚一點,甚至可以在尖峰時改為取樣記錄:只保存部分點擊事件,報表依取樣比例還原(技術上同 sampling),尖峰過後再恢復全量。佇列滿了的時候,兩種情形都是轉址照常回應,這一點 0.24 已經決定:點擊統計失敗不能拖垮轉址。差別在點擊事件的去處:點擊只是內部參考時,可以丟棄或取樣;點擊分析是付費功能時,0.23 要求事件不能遺失,事件要改寫到另一個持久的緩衝,或事後由 CDN 與負載平衡器的存取紀錄補回。
爆紅的連結也會帶來一波非人為請求。連結被貼進聊天軟體、社群平台時,平台會抓取網址產生預覽;多數平台在伺服器端依網址快取預覽,同一個網址只抓少數幾次;由傳送者自己的裝置產生預覽的平台,預覽抓取會隨轉發次數增加,而且集中在連結剛開始擴散的那段時間。0.24 的〈預覽抓取與安全掃描等非人為請求的標記〉把這類請求依 User-Agent、請求方法與來源網段標記出來、分開計數。爆紅時這份標記決定了客戶看到的點擊數有多少是真人,所以標記規則要在平常就涵蓋主要的聊天與社群平台。
目的地網站承接匯集點擊的能力
短網址服務撐住了,流量仍然要到達目的地網站,而目的地網站往往比短網址服務小得多。爆紅連結把所有點擊匯集到同一個目的地,對方的伺服器可能因此回應變慢或錯誤。這時短網址服務的監控看到的是轉址全部成功,看不到使用者實際到不到得了目的地。
目的地網站承接不了匯集的點擊時,短網址服務能做的有限,因為目的地不在它的控制範圍內。它能做的是兩件事:在點擊報表上說明點擊數是轉址數,不等於成功到達目的地的人數;以及在建立介面與文件裡提醒客戶,把一條短網址放進大量曝光的位置之前,目的地網站要先依 9.11 高峰事件準備 準備承接的容量。
爆紅的連結是惡意連結時:快取與撤回的衝突
爆紅最危險的情形是那條連結指向釣魚頁或惡意軟體。0.24 的濫用防護在確認惡意之後把對照表的狀態改成已停用,而停用多快對所有點擊生效,由各層快取的保存時間決定。〈爆紅連結的負載落點:CDN、程序內快取與對照表〉那一節為了扛住爆紅,要讓轉址回應盡量被 CDN 與程序內快取保存;撤回惡意連結時為了盡快生效,要讓轉址回應盡量不被保存,兩個目標在同一條連結上衝突。
這個衝突最尖銳的是 CDN 有保存轉址的時候(不統計點擊,或依短碼切換保存的做法);點擊分析是付費功能、CDN 不保存時,同樣的取捨落在程序內快取的 TTL 上,撤回的時效由它與瀏覽器的保存時間決定。CDN 有保存時,兩者的折衷是保存時間短、而且能主動清除。CDN 保存轉址的時間設在幾秒到幾十秒,同時保留依路徑主動清除(purge)單一短碼的能力(cache invalidation);程序內快取在沒有建立廣播失效機制時無法從中央清除,所以它的 TTL 要短到符合需求定義的撤回時限;瀏覽器那一層只能靠轉址回應的快取標頭事先限制,301 不帶快取標頭時瀏覽器可能永久保存,已經點過的人之後看不到停用後的警告頁(見 12.8 快取設定的應用:轉址、需登入的 API 回應與個人化頁面)。爆紅期間的請求量大,幾秒的保存時間就足以讓絕大部分點擊由快取回應,所以短的保存時間對扛流量的效果損失不大,換來的是 CDN 這一層的停用能在幾秒內生效;整體撤回時間仍要加上程序內快取的 TTL 與瀏覽器的保存時間。
建立路徑被機器人大量建立連結
建立路徑的瞬時大流量通常來自機器人:用大量帳號或來源位址建立連結,用來散布釣魚頁、垃圾訊息或搜尋引擎垃圾連結。它和爆紅不同的地方在於這些請求本身就是要擋的對象,處置的方向是辨識並拒絕,而不是承接。
機器人大量建立連結的成本不只是一時的流量,下列三項在攻擊結束後仍然留著:
- 儲存:每一條建立成功的連結都進入對照表,依需求定義的保存承諾可能要永久保存,即使事後刪除,短碼也要留一筆佔位紀錄不能重用(見 0.24 的〈對照表:儲存選型、欄位與分片〉)。
- 濫用檢查的配額:建立時與建立後的黑名單查詢通常呼叫外部的網址信譽服務,這些服務本身有查詢配額,機器人在短時間內建立大量連結,會把配額用完,檢查排不進去的連結越積越多。檢查佇列因此要有優先順序:依點擊量排序,正在爆紅的連結先重查,或另外保留一份配額給它們,否則機器人用光配額的期間,爆紅中的釣魚連結也跟著排隊。網址信譽服務也有可以下載到本機比對的清單形式,本機比對沒有每次查詢的配額;外部查詢只用在本機清單判斷不了的情形。0.24 的〈濫用防護:建立時的檢查、建立後的重查與轉址時的比對〉為「連危險網址清單都還沒取得」的情形,給了兩種情形的選項:開放匿名建立的服務,對最近建立、還沒通過背景重查的連結阻擋;開放註冊或經由 API 代建的服務,只對其中建立者帳號或金鑰還沒有紀錄的連結阻擋。檢查積壓時,這些連結同樣還沒被檢查過,所以同一條規則可以延伸過來;本篇延伸時把範圍收在匿名與沒有紀錄的建立者:只擋匿名或沒有紀錄的建立者在積壓期間建立的連結,付費客戶與高優先等級的 API 金鑰(例如簡訊發送前產生的連結)不在範圍內。被擋的連結顯示一頁「連結正在檢查中」的說明,檢查通過後恢復轉址;這需要記錄每條連結是否已通過檢查,例如另設一個最後通過檢查的時間,轉址依它顯示檢查中的頁面;0.24 的阻擋規則本身就需要這個欄位,它和正常、已停用、已刪除這個狀態欄是兩個維度,合在同一欄會遇到「檢查中的連結被刪除時填哪一個」這類衝突。
- 網域名聲:只要有一部分惡意連結在被停用之前轉址出去,服務的轉址網域就可能被郵件過濾器與瀏覽器列入封鎖清單,所有客戶的正常連結一起受影響(0.24 的〈濫用防護:建立時的檢查、建立後的重查與轉址時的比對〉)。
依單一來源位址或單一帳號限制建立速率(rate limit)擋得住集中的攻擊,擋不住分散在大量位址與帳號上的攻擊:每一個來源都在配額之內,總量仍然暴增。分散的攻擊要靠准入控制(Admission Control),以及提高每建立一個帳號或一條連結的成本:
- 限制誰能建立:攻擊期間關閉匿名建立,只讓已登入、信箱已驗證、或有付費紀錄的帳號建立。0.23 的〈建立者的範圍與濫用處理〉在需求層決定了平常誰能建立,這裡是把那個範圍在攻擊期間暫時收窄。這個切換要做成一個設定開關(feature flag,維運用的開關見 6.17 Feature Flag Governance 的〈Kill switch 設計〉),平常就存在並演練過,攻擊時只是撥動它。
- 限制新帳號的額度:新註冊、還沒有任何紀錄的帳號給較低的建立額度,帳號累積一段時間的正常建立與點擊紀錄之後再放寬,讓大量新註冊的帳號各自只能建立少量連結。
- 依目的地限速:攻擊者用大量連結散布同一批頁面,所以對同一個目的地在單位時間內新建的連結數設上限,不論建立者是誰。門檻依各網域平常的建立量設定,熱門網域(影音、購物、文件服務)本來就有大量正常連結,要另設較高門檻;攻擊頁放在共用託管平台的網域下時,依網域計數會連帶擋到平台的正常用戶,要改用完整網址或路徑計數。
- 建立時加挑戰:匿名建立時要求通過挑戰(例如驗證碼),直接提高每一條連結的建立成本;這只適用於瀏覽器上的建立,經由 API 建立的請求要靠金鑰與額度。
什麼時候撥動准入開關、收緊新帳號額度,依據的是攻擊在建立事件上留下的形狀。新註冊帳號的數量在幾分鐘內跳升,而正常的註冊隨行銷活動漸增;新建立的連結大多指向少數幾個網域,而且遠超過那幾個網域平常的建立量,因為攻擊者用大量連結散布同一批頁面,正常客戶的目的地是分散的;黑名單命中率上升,因為攻擊的目的地正是要被封鎖的那一類。新建立的連結幾乎都沒有被點擊,也是攻擊的形狀,但它是落後訊號,要等一段時間才觀察得到,用來事後確認;即時的判斷靠帳號數、目的地集中度與黑名單命中率。匿名建立沒有帳號,帳號數這個訊號不會觸發,要改看單位時間的匿名建立量與來源網段的分布;黑名單命中率依賴網址信譽服務,配額被用完時這個訊號跟著失效,這時剩下的即時訊號是目的地集中度與匿名建立量。這些訊號在平常就要能從建立事件裡算出來。
建立路徑與轉址路徑也要在容量上互相隔離。0.24 已經把管理介面放在另一個可註冊網域(registrable domain);容量上,建立路徑的請求不能和轉址路徑共用同一組應用伺服器與連線池而互相排擠,否則對建立路徑的攻擊會拖慢所有既有連結的轉址。把兩條路徑放在各自的伺服器群組與資源配額裡(bulkhead),對建立路徑的攻擊就不會佔用轉址的伺服器。兩條路徑仍然共用對照表,所以資料層也要隔離:轉址讀副本或快取,建立的寫入另外限速,避免大量寫入拖慢轉址的讀取。寫入暴增時副本的延遲會上升,0.24 的做法裡副本查無時要回主節點確認,回主節點的請求也會跟著變多,所以這段回主節點的流量也要限速,副本查不到、必須回主節點確認的,只有剛建立、還沒複製到副本的短碼,所以可以只讓這些短碼回主節點;遞增計數器發號時,比對已發出號段的上界就能判斷一個短碼是不是新的,隨機產生的短碼做不到這件事。另一個做法不依賴發號方式:建立連結時同時把新的對照寫進共用快取,轉址先查快取,就不必依賴副本查到新建立的短碼。
優先等級的機制見 9.15 的〈負載卸除與限流的分工、請求的優先等級〉;短網址服務裡各路徑的等級由 0.23 短網址服務的需求定義:自建或購買、保存期限、目的地修改與濫用處理的規格 的〈建立路徑與轉址路徑的可用性分級〉決定:轉址最高,建立、報表查詢與管理介面較低,整體過載時先卸除較低的那幾類。例外是嵌在另一條寫入路徑裡的建立(發文當下改寫連結、送出簡訊前才產生連結),它的可用性跟著那條路徑,要依 API 金鑰或用途標成較高的等級,否則卸除建立會擋住整批發送。
轉址路徑的掃描與體積型攻擊
轉址路徑上的攻擊有掃描與體積型攻擊兩種形狀。掃描是程式大量請求隨機或連續的短碼,嘗試撞到別人的連結。0.24 的〈各層快取與撤回所需的時間〉已經處理它的負載面:不存在的短碼寫進短 TTL 的 negative cache,避免每次都打到對照表,單一來源的 404 超過門檻時限制它的速率。掃描能撞到多少連結,則由短碼生成的方式決定,見 0.24 的〈短碼生成:可猜測性與協調成本的取捨〉。
體積型攻擊對準轉址網域,在網路層塞滿入口,應用層的快取與限流都位在被塞滿的入口之後,攻擊流量在到達它們之前就已經佔滿頻寬。轉址網域平常就放在 CDN 或雲端業者的邊緣網路後面,攻擊流量由邊緣吸收,前提是 origin 只接受邊緣網路來源的連線、位址不外洩,見 9.15 的〈體積型攻擊(DDoS)的處置:流量清洗與邊緣網路〉。轉址網域與管理介面分屬不同網域,再加上兩者解析到不同的入口與後端時,轉址網域遭受攻擊期間,客戶仍然登得進管理介面查看狀態;只是網域不同、入口相同時,攻擊照樣會打到管理介面。
爆紅、建立濫用與轉址攻擊事前要建好的機制
爆紅、建立路徑的濫用與轉址路徑的攻擊,當下能做的決定都很少:對爆紅的短碼開啟 CDN 保存、撥動關閉匿名建立的開關、清除一條惡意連結的快取。每一個決定都依賴一個平常就存在的機制:依短碼切換 CDN 保存規則與從存取紀錄補回點擊、依路徑清除 CDN 的能力、建立路徑的准入開關與新帳號額度、建立事件裡的攻擊訊號、轉址與建立兩條路徑在容量上的隔離、以及放在邊緣網路後面的轉址網域。這些機制和 0.24 的實作一起建好,瞬時大流量來的時候才只剩下決定要做。
延伸閱讀:通用機制、需求定義、限流與快取標頭
- 流量來源的辨識、擴展緩衝、負載卸除、准入控制與體積型攻擊的通用機制:9.15 無預警瞬時大流量:流量來源辨識、擴展緩衝、請求優先等級、准入控制、體積型攻擊與退路。
- 誰能建立連結、點擊資料的地位、建立與轉址的可用性分級:0.23 短網址服務的需求定義:自建或購買、保存期限、目的地修改與濫用處理的規格。
- 依帳號、來源位址或 API 金鑰分配建立額度的演算法:Rate Limit 實作。
- 轉址回應的狀態碼與快取標頭怎麼影響撤回:12.8 快取設定的應用:轉址、需登入的 API 回應與個人化頁面。
#backend #service-selection #url-shortener #surge #system-design