考慮一個惡意行為者試圖通過API進行數(shù)據(jù)注入、抓取、收集或泄露的情況。這類惡意活動通常以行為者以特定順序向API端點發(fā)起請求。此外,僅僅使用容量技術(shù)往往無法輕易檢測到這些惡意活動,因為行為者可能故意緩慢地執(zhí)行API請求,以試圖規(guī)避容量濫用保護。因此,為了可靠地防止這種惡意活動,我們需要考慮API請求的順序。我們使用序列濫用這個術(shù)語來指代惡意的API請求行為。因此,我們的基本目標是區(qū)分惡意和合法的API請求序列。
在本篇文章中,您將了解我們?nèi)绾螒獙椭蛻舯Wo其API免受序列濫用的挑戰(zhàn)。為此,我們將介紹目前支撐我們序列分析產(chǎn)品的統(tǒng)計機器學習(ML)技術(shù)。我們將在此前一篇博客文章提供的序列分析概括性介紹基礎上進行深入探討。
API會話
在之前的博客文章中提到過,我們來考慮一下由某個用戶發(fā)起一系列按時間順序排列的HTTP API請求的概念。發(fā)起請求是因為該用戶與一項服務進行交互,例如瀏覽一個網(wǎng)站或使用一個移動應用。我們將用戶按時間順序發(fā)出的一系列API請求稱為一個會話。舉一個熟悉的例子,客戶與銀行服務交互的會話可能如下所示:
我們的目標之一是,通過自動建議適用于我們的序列緩解產(chǎn)品的規(guī)則,強制執(zhí)行期望的序列行為,從而幫助客戶保護他們的API。如果我們強制執(zhí)行預期的行為,就可以防止不想要的順序行為。在我們的例子中,期望的序列行為可能意味著/api/v1/auth必須始終先于/api/v1/accounts/{account_id}執(zhí)行。
我們必須解決的一個重要挑戰(zhàn)是,可能的會話數(shù)量會隨著會話長度的增加而迅速增長。要理解為什么,我們可以考慮用戶與示例銀行服務交互的其他方式:例如,用戶可以任何順序執(zhí)行多次轉(zhuǎn)賬,和/或查看多個賬戶的余額。假設有3個可能的端點,下圖說明了用戶與銀行服務交互時可能的會話:
由于可能的會話數(shù)量龐大,建議緩解規(guī)則需要我們解決一個挑戰(zhàn),即從過去的會話數(shù)據(jù)中總結(jié)序列行為作為中間步驟。在我們的例子中,我們將會話中一系列連續(xù)端點(例如/api/v1/accounts/{account_id}→/api/v1/transferFunds)稱為序列。具體來說,我們需要解決的一個挑戰(zhàn)是,創(chuàng)建規(guī)則所需的順序行為并不一定僅從數(shù)量上就能明顯看出:例如,/api/transferFunds幾乎總是發(fā)生在在/api/v1/accounts/{account_id}之后,但與序列/api/v1/auth→/api/v1/accounts/{account_id}相比,序列/api/v1/accounts/{account_id}→/api/v1/transferFunds可能相對少見。因此,如果我們僅根據(jù)數(shù)量進行總結(jié),可能會認為序列/api/v1/accounts/{account_id}→/api/v1/transferFunds不重要,而實際上我們應該將其作為潛在規(guī)則挖掘出來。
從API會話中學習重要的序列
馬爾可夫鏈(Markov chain)是一種適用于序列數(shù)據(jù)的廣泛建模方法,其中我們會話數(shù)據(jù)中每個端點的概率僅取決于先行端點的固定數(shù)量。首先,我們將展示如何將標準馬爾可夫鏈應用于會話數(shù)據(jù),同時指出它們的一些局限性。其次,我們將展示如何使用一種不太知名但強大的馬爾可夫鏈來確定重要的序列。
為便于說明,我們假設我們的會話數(shù)據(jù)中有3個可能的端點。我們將使用字母a、b和c來表示這些端點:
-a:/api/v1/auth
-b:/api/v1/accounts/{account_id}
-c:/api/v1/transferFunds
在最簡單的形式下,馬爾可夫鏈不過是一個表格,它告訴我們在知道前一個字母的情況下,下一個字母的概率。如果我們使用最簡單的馬爾可夫鏈為過去的會話數(shù)據(jù)建模,我們最終可能會得到這樣的表:
表1
表1列出了馬爾可夫鏈的參數(shù),即在已知會話中前一個端點的情況下,觀察到a、b或c作為會話中下一個端點的估計概率。例如,第3行值為0.67的格表示:在已知前一個端點為c的情況下,觀察到b作為會話中的下一個端點的估計概率為67%,無論c之前是否有任何端點。因此,表中的每個條目對應于兩個端點的序列。括號中的值是我們在過去的會話數(shù)據(jù)中看到每個雙端點序列的次數(shù),用于計算表中的概率。例如,值0.01是計算分數(shù)169/(1555+13718+169)的結(jié)果。這種估計概率的方法稱為最大似然估計。
為了確定重要的序列,我們依賴于可信區(qū)間(而非最大似然估計)來估計概率??尚艆^(qū)間不產(chǎn)生單點估計(如上所述),而是表示一個合理的概率范圍。這個范圍反映了可用的數(shù)據(jù)量,即每行中序列出現(xiàn)的總數(shù)。數(shù)據(jù)越多,可信區(qū)間越窄(反映確定性程度較高),數(shù)據(jù)越少,可信區(qū)間越寬(反映確定性程度較低)。因此,根據(jù)上表中括號內(nèi)的值,我們可能會獲得以下可信區(qū)間(粗體條目將在下文中進一步解釋):
表2
為簡潔起見,我們不會在這里演示如何手動計算出可信區(qū)間(它們涉及評估beta分布的分位數(shù)函數(shù))。盡管如此,修訂后的表格顯示更多數(shù)據(jù)導致可信區(qū)間縮小:注意第一行總共出現(xiàn)15442次,而第二行總共出現(xiàn)328084次。
為了確定重要的序列,我們使用比上述稍微復雜一些的馬爾可夫鏈。作為中間步驟,我們先考慮每個表項對應三個端點序列的情況(而不是上面提到的兩個),以下是一個示例表格:
表3
表3再次列出觀察到a、b或c作為會話中下一個端點的估計概率,但基于已知會話中的前兩個端點。也就是說,第3行區(qū)間值為0.09-0.13的格意味著,已知前兩個端點為ca時,觀察到a作為下一個端點的概率具有從9%到13%的可信區(qū)間,無論ca之前是否有任何端點。用行話來說,我們說上表代表一個二階馬爾可夫鏈。這是因為表中的項表示,在知道前兩個端點作為上下文的情況下觀察到下一個端點的概率。
作為一個特例,零階馬爾可夫鏈僅僅表示會話中接口的分布。我們可以將“空上下文”對應的一行的概率列表如下:
表4
請注意,表4中的概率并不僅僅代表會話中沒有先行端點的情況。相反,概率是會話中端點出現(xiàn)的情況,適用于一般情況下,我們不知道前面的端點,也不管以前出現(xiàn)了多少個端點。
回到我們識別重要序列的任務,一種可能的方法可能是簡單地使用某個固定階數(shù)N的馬爾可夫鏈。例如,如果我們將表3中可信區(qū)間下限的閾值設為0.85,則總共會保留3個序列。另一方面,這種方法有兩個值得注意的限制:
1.我們需要一種方法為模型階數(shù)N選擇合適的值。
2.由于模型階保持固定,則已識別序列均具有相同的長度N+1。
變階馬爾可夫鏈
變階馬爾可夫鏈(VOMC)是上述固定階馬爾可夫鏈的更強大擴展,它解決了上述限制。VOMC利用這樣一個事實,即對于固定階數(shù)N的馬爾可夫鏈的某些選定值,概率表可能包括統(tǒng)計冗余信息:讓我們比較一下上面的表3和表2,并考慮表3中對應上下文aa、ba、ca(這3個上下文共享a作為后綴)的粗體行。
對于所有3個可能的下一個端點a,b,c,這些行給出了可信區(qū)間,這些區(qū)間與表2中對應上下文a的各自估計重疊(也以粗體表示)。我們可以將這些重疊的區(qū)間解釋為,在已知a為先行端點的情況下,概率估計之間不存在明顯的差異。由于a的先行端點是什么對下一個端點的概率沒有明顯影響,我們可以認為表3中的這3行是冗余的:我們可以通過替換成表2中對應于上下文a的行來“折疊”它們。
按所述修改表3的結(jié)果如下(新行以粗體表示):
表5
表5代表一個VOMC,因為上下文長度可變:在示例中,上下文長度為1和2。由此可見,表中的條目表示長度在2到3個端點之間變化的序列,具體取決于上下文長度。對所述折疊上下文的方法進行推廣,可以得到如下在離線環(huán)境中學習一種VOMC的算法草圖:
(1)定義包含會話中下一個端點的估計概率的表T,給定會話中可選的0,1,2,…,N_max個端點。也就是說,通過連接對應于固定階數(shù)0,1,2,…,N_max的馬爾可夫鏈的行來形成一個表。
(2)is_modified:=true
(3)DO WHILE is_modified
(4)D:=T中不是T中至少1個其他上下文后綴的所有上下文
(5)is_modified=false
(6)FOR ctx IN C
(7)IF length(ctx)>0
(8)parent_ctx:=通過刪除ctx中最左邊端點而獲得的上下文。
(9)IF is_collapsible(ctx,parent_ctx)
(10)修改T,丟棄ctx
(11)is_modified=true
在以上偽代碼中,length(ctx)是上下文ctx的長度。在第9行,is_colrapsible()涉及比較上下文ctx和parent_ctx,按照生成表5所描述的方式:is_colrapsible()評估為true,當且僅當在我們?yōu)槊恳粋€可能的下一個端點比較上下文ctx和parent_ctx時,觀察到所有可信區(qū)間重疊。最大序列長度為N_max+1,其中N_max是某個常量。在第4行,如果我們可以通過在上下文q前添加零個或多個端點來構(gòu)成另一個上下文p,則q是p的后綴。(根據(jù)這個定義,上面提到的0階模型的“空上下文”是T中所有上下文的后綴。)上述算法草圖是Rissanen【1】和Ron等人首先提出的想法的一個變體?!?】。
最后,我們將結(jié)果表T中的條目作為我們的重要序列。因此,應用VOMC的結(jié)果是一組我們認為重要的序列。然而,對于序列分析,我們認為對序列進行排名也很有用。我們通過計算一個0.0到1.0之間的“優(yōu)先級分數(shù)”來做到這一點,即序列中出現(xiàn)的次數(shù)除以序列中最后一個端點的出現(xiàn)次數(shù)。因此,優(yōu)先順序分數(shù)接近1.0表示給定端點幾乎總是在序列中的其余端點之前。通過這種方式,手動檢查最高分序列就成為了在我們的序列緩解產(chǎn)品中創(chuàng)建優(yōu)先規(guī)則的一種半自動啟發(fā)式方法。
大規(guī)模學習序列
以上內(nèi)容是我們在序列分析中使用的統(tǒng)計機器學習技術(shù)的高層概述。實際上,我們設計了一種高效的算法,它不需要事先的訓練步驟,而是隨著數(shù)據(jù)到達不斷更新模型,并生成重要序列的頻繁更新摘要。這種方法使我們能夠克服在此博客文章中未涉及的內(nèi)存成本等額外挑戰(zhàn)。最重要的是,上述算法草圖的簡單實現(xiàn)仍會導致表中的行數(shù)(上下文)隨著最大序列長度的增加而激增。我們需要解決的另一個挑戰(zhàn)是確保我們的系統(tǒng)能夠處理海量API,而不會對CPU負載產(chǎn)生不利影響。我們在前期使用了一種水平可擴展的自適應采樣策略,以便對海量API進行更激進的采樣。我們的算法隨后處理這些采樣的API請求流。在客戶入駐后,序列會隨著時間的推移進行組裝和學習,因此重要序列的當前摘要表示一個大約24小時的回顧區(qū)間。序列分析進一步將序列存儲在Clickhouse中,并通過一個GraphQL API和Cloudflare儀表板顯示它們。希望實施序列規(guī)則的客戶可以使用序列分析來執(zhí)行此操作。序列分析負責確保規(guī)則在Cloudflare的全球網(wǎng)絡上以分布式方式共享和匹配,這是另外一個令人興奮的話題,我們將在未來的博客文章中討論。
下一步
現(xiàn)在,您已經(jīng)對我們?nèi)绾谓沂局匾腁PI請求序列有了更好的理解,請繼續(xù)關注本系列后續(xù)的博客文章,我們將描述我們?nèi)绾伟l(fā)現(xiàn)客戶可能想要阻止的異常API請求序列。目前,API Gateway客戶可以通過兩種方式開始:使用序列分析來探索重要的API請求序列,使用序列緩解來強制執(zhí)行API請求序列。