![](https://img.eservicesgroup.com.cn/images/bussiness/platform/icon_platform_value.png)
![](https://img.eservicesgroup.com.cn/images/bussiness/platform/icon_platform_value_active.png)
Cloudflare的HTML解析歷史(上),cloudflare的api解析Cloudflare的HTML解析歷史(上)什么是HTML流量重寫(xiě)器?HTML流量 重寫(xiě)器接受HTML字符串或字節(jié)流輸入,將其解析為令牌或任何其他結(jié)構(gòu)化中間表示(IR),例如抽象語(yǔ)法樹(shù)(AST)。然后,它在轉(zhuǎn)換回HTML之前對(duì)標(biāo)記執(zhí)行轉(zhuǎn)換。這......
什么是HTML流量重寫(xiě)器?
HTML流量 重寫(xiě)器接受HTML字符串或字節(jié)流輸入,將其解析為令牌或任何其他結(jié)構(gòu)化中間表示(IR),例如抽象語(yǔ)法樹(shù)(AST)。然后,它在轉(zhuǎn)換回HTML之前對(duì)標(biāo)記執(zhí)行轉(zhuǎn)換。這就提供了在處理字節(jié)時(shí)修改,提取或添加到現(xiàn)有HTML文檔的功能。將其與標(biāo)準(zhǔn)的HTML樹(shù)解析器進(jìn)行比較,后者需要檢索整個(gè)文件以生成完整的DOM樹(shù)?;跇?shù)的重寫(xiě)器將花費(fèi)更長(zhǎng)的時(shí)間來(lái)交付第一個(gè)處理的字節(jié),并且需要更多的內(nèi)存。
HTML重寫(xiě)器
例如你擁有一個(gè)擁有很多歷史內(nèi)容的大型網(wǎng)站,現(xiàn)在希望通過(guò)HTTPS來(lái)提供該網(wǎng)站。你將很快遇到通過(guò)HTTP提供資源(圖像、腳本、視頻)的漏洞,因?yàn)檫@種“混合內(nèi)容”打開(kāi)了一個(gè)安全漏洞,瀏覽器將警告或阻止這些資源。這意味著,更新網(wǎng)站每個(gè)頁(yè)面上的每個(gè)鏈接可能很困難,甚至不可能。使用HTML流量重寫(xiě)器,你可以選擇任何HTML標(biāo)記的URI屬性,并將任何HTTP鏈接更改為HTTPS。研究人員在2016年構(gòu)建了此功能,即自動(dòng)HTTPS重寫(xiě)以解決客戶(hù)的混合內(nèi)容問(wèn)題。
下文,我將詳細(xì)介紹如何從在HTML頁(yè)面中查找電子郵件地址的簡(jiǎn)單想法開(kāi)始,以構(gòu)建幾乎符合規(guī)范的HTML解析器,然后到匹配虛擬機(jī)的CSS選擇器的旅程。
在邊緣重寫(xiě)
通過(guò)Cloudflare重寫(xiě)內(nèi)容時(shí),我們不想影響網(wǎng)站性能。設(shè)計(jì)HTML流量重寫(xiě)器的平衡在于,通過(guò)保留盡可能少的信息,同時(shí)保留重寫(xiě)匹配標(biāo)記的能力,來(lái)最大程度地減少響應(yīng)字節(jié)流中的暫停。
與瀏覽器中使用的HTML解析器相比,要求的差異包括:
輸出延遲
對(duì)于瀏覽器來(lái)說(shuō),文檔對(duì)象模型(DOM)是解析過(guò)程的最終目的,但在本例中,我們必須解析,重寫(xiě)并序列化回HTML。對(duì)于Cloudflare的反向代理,邊緣服務(wù)器上的任何內(nèi)容處理都會(huì)導(dǎo)致服務(wù)器與眼球之間的延遲。要最小化HTML處理的延遲影響,這涉及解析,重寫(xiě)和序列化回HTML。在所有這些階段中,我們都希望盡可能快地將延遲最小化。
什么是邊緣服務(wù)器
隨著互聯(lián)網(wǎng)及其應(yīng)用的快速發(fā)展,絕大多數(shù)企業(yè)都建立自己的網(wǎng)站,增強(qiáng)對(duì)外聯(lián)絡(luò),加速業(yè)務(wù)流程,客戶(hù)對(duì)網(wǎng)站系統(tǒng)訪問(wèn)的響應(yīng)時(shí)間,網(wǎng)站內(nèi)容以及所提供服務(wù)的可靠性,即時(shí)性等要求也越來(lái)越高,使得以單臺(tái)服務(wù)器來(lái)支撐整個(gè)網(wǎng)站的系統(tǒng)已無(wú)法滿(mǎn)足客戶(hù)需求,取而代之的是采用兩到三層架構(gòu)的一組服務(wù)器.第一層是跟用戶(hù)直接發(fā)生聯(lián)系的前端服務(wù)器,也稱(chēng)為邊緣服務(wù)器。
邊緣服務(wù)器為用戶(hù)提供一個(gè)進(jìn)入網(wǎng)絡(luò)的通道和與其它服務(wù)器設(shè)備通訊的功能,通常邊緣服務(wù)器是一組完成單一功能的服務(wù)器,如防火墻服務(wù)器,高速緩存服務(wù)器,負(fù)載均衡服務(wù)器,DNS服務(wù)器等。第二層是中間層,也稱(chēng)為應(yīng)用服務(wù)器,包括Web表現(xiàn)服務(wù)器,Web應(yīng)用服務(wù)器等.第三層是后端數(shù)據(jù)庫(kù)服務(wù)器。
解析器的加載量
通常情況下,瀏覽器很少需要處理大小超過(guò)1Mb的HTML頁(yè)面,并且平均頁(yè)面加載時(shí)間最多約為3s。 HTML解析不是頁(yè)面加載過(guò)程的主要瓶頸,因?yàn)闉g覽器在運(yùn)行腳本和加載其他關(guān)鍵用戶(hù)資源時(shí)會(huì)被阻塞。我們可以粗略估計(jì),對(duì)于瀏覽器的HTML解析器來(lái)說(shuō),大約3Mbps的加載量是可以接受的。在Cloudflare,每個(gè)CPU擁有數(shù)百兆的流量,因此我們需要一個(gè)解析器,其速度要快一個(gè)數(shù)量級(jí)。
內(nèi)存限制
比如簡(jiǎn)單的HTML標(biāo)記在瀏覽器中打開(kāi)時(shí),將消耗大量系統(tǒng)內(nèi)存,最后終止瀏覽器選項(xiàng)卡(解析器將消耗所有這些內(nèi)存):
不幸的是,即使對(duì)于HTML流量重寫(xiě),也不可避免地需要緩沖部分輸入??紤]以下兩個(gè)HTML代碼段:
當(dāng)在HTML頁(yè)面末尾遇到HTML時(shí),這些看似相似的HTML片段將被完全區(qū)別對(duì)待。第一個(gè)片段將被解析為開(kāi)始標(biāo)記,第二個(gè)片段將被忽略。僅通過(guò)查看 字符后的標(biāo)記名,解析器無(wú)法確定是否找到了開(kāi)始標(biāo)記。它需要遍歷搜索結(jié)束“”的輸入以做出決定,緩沖中間的所有內(nèi)容,以便稍后將其作為開(kāi)始標(biāo)簽令牌發(fā)快遞給使用者。
這一要求迫使瀏覽器在最終放棄內(nèi)存不足漏洞之前無(wú)限期地緩沖內(nèi)容,在本文的示例中,我們無(wú)法花費(fèi)數(shù)百兆的內(nèi)存來(lái)解析單個(gè)HTML文件(實(shí)際的限制甚至更嚴(yán)格,甚至每個(gè)請(qǐng)求使用十幾KB都是不可接受的)。就內(nèi)存使用而言,我們需要比其他實(shí)現(xiàn)復(fù)雜得多,并且可以?xún)?yōu)雅地處理所有提供的內(nèi)存容量不足以完成解析的情況。
v0:隨機(jī)(Adhoc)解析器
查找和模糊電子郵件
2010年,Cloudflare決定提供一項(xiàng)功能,以阻止流行的電子郵件抓取工具。這種保護(hù)的基本思想是查找和模糊頁(yè)面上的電子郵件,然后使用注入的JavaScript代碼在瀏覽器中將其解碼回去。聽(tīng)起來(lái)很簡(jiǎn)單,對(duì)吧?你搜索任何看起來(lái)像電子郵件的東西,對(duì)其進(jìn)行編碼,然后使用一些JavaScript魔術(shù)對(duì)其進(jìn)行解碼,然后將結(jié)果呈現(xiàn)給最終用戶(hù)。
但是,即使這樣看似簡(jiǎn)單的任務(wù)也已經(jīng)需要解決幾個(gè)問(wèn)題。首先,我們需要定義什么是電子郵件。實(shí)際上,甚至臭名昭著的RFC都涵蓋了整個(gè)RFC,實(shí)際上,它已經(jīng)過(guò)時(shí)且不完整,因?yàn)樾翿FC添加了許多有效的電子郵件構(gòu)造,包括Unicode支持?,F(xiàn)在,讓我們關(guān)注一個(gè)更高層次的問(wèn)題:轉(zhuǎn)換流量?jī)?nèi)容。
來(lái)自網(wǎng)絡(luò)的內(nèi)容以數(shù)據(jù)包的形式出現(xiàn),這些數(shù)據(jù)包必須由我們的服務(wù)器緩沖并解析為HTTP。你無(wú)法預(yù)測(cè)內(nèi)容的分割方式,這意味著你始終需要對(duì)其中的某些內(nèi)容進(jìn)行緩沖,因?yàn)橐鎿Q的內(nèi)容可以存在于多個(gè)輸入塊中。
假設(shè)我們決定使用簡(jiǎn)單的正則表達(dá)式,比如 [\w.]+@[\w.]+ 。如果通過(guò)的內(nèi)容包含電子郵件“test@example.org”,它可能被分成以下幾塊:
為了保持首字節(jié)時(shí)間(TTFB)和一致的速度,我們希望確保在確定前一個(gè)塊不適合用于替換時(shí)立即釋放它。
最簡(jiǎn)單的方法是將正則表達(dá)式轉(zhuǎn)換為狀態(tài)機(jī)或有限自動(dòng)機(jī),盡管你可以手動(dòng)完成此操作,但最終將獲得難以維護(hù)且易于出錯(cuò)的代碼。相反,選擇了Ragel將正則表達(dá)式轉(zhuǎn)換為有效的本機(jī)狀態(tài)機(jī)代碼。 Ragel除了遍歷狀態(tài)機(jī)外,不會(huì)嘗試緩沖或做其他任何事情。它提供的語(yǔ)法不僅可以描述模式,還可以將自定義操作(以宿主語(yǔ)言編寫(xiě)的代碼)與任何給定狀態(tài)相關(guān)聯(lián)。
在本文的示例中,我們可以通過(guò)緩沖區(qū),直到匹配電子郵件的開(kāi)頭。如果我們隨后發(fā)現(xiàn)該模式不是電子郵件,則可以在該模式停止匹配后立即退出緩沖。否則,我們可以檢索匹配的電子郵件并將其替換為新內(nèi)容。
要將模式轉(zhuǎn)換為流解析器,我們可以記住電子郵件的可能開(kāi)頭的位置,除非已將其丟棄或由當(dāng)前輸入的末尾替換,否則將未處理的部分存儲(chǔ)在永久緩沖區(qū)中。然后,當(dāng)出現(xiàn)一個(gè)新塊時(shí),我們可以對(duì)其進(jìn)行單獨(dú)處理,從Ragel記住自己的狀態(tài)恢復(fù),但隨后使用緩沖的塊和一個(gè)新塊來(lái)發(fā)出或進(jìn)行模糊。
現(xiàn)在,我們已經(jīng)解決了在文本中匹配電子郵件模式的問(wèn)題,我們需要處理一個(gè)事實(shí),即它們需要在頁(yè)面上進(jìn)行模糊,這是第一次引入HTML“解析”的提示。
我將“解析”放在引號(hào)中,因?yàn)殡娮余]件過(guò)濾器(模塊的名稱(chēng))并沒(méi)有實(shí)現(xiàn)整個(gè)解析器,而是試圖復(fù)制整個(gè)HTML語(yǔ)法,而是添加了自定義的Ragel模式,僅用于跳過(guò)注釋以及不應(yīng)模糊電子郵件的標(biāo)簽。
這是一種合理的方法,尤其是在2010年,HTML5規(guī)范發(fā)布的四年之前,當(dāng)時(shí)所有瀏覽器都有自己獨(dú)特的HTML處理方法。但是,你可以想象,這種方法無(wú)法很好地?cái)U(kuò)展。與此同時(shí),新的特性開(kāi)始添加,這也需要?jiǎng)討B(tài)修改HTML(比如自動(dòng)插入谷歌分析腳本),現(xiàn)有的模塊似乎是最好的地方。它發(fā)展到能夠處理越來(lái)越多的標(biāo)記、操作和語(yǔ)法邊緣情況。
在2011年,Cloudflare決定還添加縮小功能,即使用戶(hù)自己沒(méi)有使用縮小功能也能加快其網(wǎng)站的速度。為此,我們決定使用現(xiàn)有的流量壓縮器——jitify。它已經(jīng)具有NGINX綁定,這使其非常適合集成到現(xiàn)有管道中。
不幸的是,就像當(dāng)時(shí)的大多數(shù)其他解析器以及我們上面描述的那樣,它具有自己的HTML,JavaScript和CSS處理規(guī)則,這些規(guī)則并不精確,而是試圖盡最大努力解析內(nèi)容。這導(dǎo)致我們擁有兩個(gè)不兼容的獨(dú)立流解析器,并且可能單獨(dú)或組合生成漏洞。
v1:符合HTML5規(guī)范的解析器
多年來(lái),工程師一直在向不斷增長(zhǎng)的狀態(tài)機(jī)中添加新功能,同時(shí)修復(fù)了由于語(yǔ)法實(shí)現(xiàn)不精確,各種解析器之間的沖突以及功能本身存在的問(wèn)題而引起的新漏洞。
接下來(lái),我將描述研發(fā)者是如何從規(guī)范狀態(tài)機(jī)開(kāi)始構(gòu)建符合HTML5的解析器。僅使用此狀態(tài)機(jī),就應(yīng)該直接構(gòu)建解析器。你可能已經(jīng)知道,從歷史上看,HTML的解析并非十分嚴(yán)格,這意味著在不破壞現(xiàn)有實(shí)現(xiàn)的情況下,解析時(shí)需要構(gòu)建實(shí)際的DOM。這對(duì)于流量重寫(xiě)器是不可能的,因此開(kāi)發(fā)了解析器反饋的模擬器。就性能而言,最好不要做任何事情。然后,我們描述了為什么重寫(xiě)器在重寫(xiě)HTML時(shí)可能會(huì)變得“懶惰”而不執(zhí)行昂貴的文本編碼和解碼,然后詳細(xì)討論了判斷響應(yīng)是否是HTML的難題。
HTML5
到2016年,HTML5已經(jīng)為解析和兼容遺留內(nèi)容和自定義瀏覽器實(shí)現(xiàn)定義了精確的語(yǔ)法規(guī)則,目前,所有瀏覽器和許多第三方實(shí)現(xiàn)都已經(jīng)實(shí)現(xiàn)了它。
HTML5解析規(guī)范以狀態(tài)機(jī)的形式定義了基本的HTML語(yǔ)法,我們已經(jīng)在Ragel上有過(guò)類(lèi)似用例的經(jīng)驗(yàn)。盡管語(yǔ)法很復(fù)雜,但規(guī)范到Ragel語(yǔ)法的翻譯卻很簡(jiǎn)單。由于能夠?qū)egex語(yǔ)法與顯式轉(zhuǎn)換混合在一起,因此代碼看起來(lái)比狀態(tài)機(jī)的形式描述更簡(jiǎn)單。
HTML5解析需要一個(gè)“DOM”
為了不破壞現(xiàn)有的實(shí)現(xiàn),HTML5被指定為針對(duì)不正確的標(biāo)簽嵌套、排序、未關(guān)閉的標(biāo)簽、缺失的屬性和所有其他在舊瀏覽器中可能出現(xiàn)的問(wèn)題的恢復(fù)過(guò)程。為了解決這些問(wèn)題,該規(guī)范要求使用樹(shù)構(gòu)建器來(lái)驅(qū)動(dòng)詞法分析器。從本質(zhì)上講,如果沒(méi)有DOM,就無(wú)法正確標(biāo)記化HTML(分割為單獨(dú)的標(biāo)簽)。
規(guī)范定義的HTML解析流程
因此,大多數(shù)解析器甚至不嘗試執(zhí)行流解析,而是將輸入作為一個(gè)整體并生成一個(gè)文檔樹(shù)作為輸出。如果不給頁(yè)面加載增加明顯的延遲,我們就無(wú)法進(jìn)行流轉(zhuǎn)換。
現(xiàn)有的HTML5 JavaScript解析器parse5已使用流量令牌生成器和重寫(xiě)器實(shí)現(xiàn)了符合規(guī)范的樹(shù)解析。為了避免必須創(chuàng)建完整的DOM,引入了“解析器反饋模擬器(parser feedback simulator)”的概念。
樹(shù)生成器的反饋機(jī)制
你可以從名稱(chēng)中猜到,該模塊旨在模擬完整解析器對(duì)令牌生成器的反饋,而無(wú)需實(shí)際構(gòu)建整個(gè)DOM,而是僅保留正確驅(qū)動(dòng)狀態(tài)機(jī)所需的必要信息和上下文。
在經(jīng)過(guò)嚴(yán)格的測(cè)試并將測(cè)試運(yùn)行器升級(jí)到parse5之后,我們發(fā)現(xiàn)這種技術(shù)適用于網(wǎng)絡(luò)上大多數(shù)編寫(xiě)得很差的頁(yè)面,并將其應(yīng)用到LazyHTML中。
LazyHTML架構(gòu)
下一篇文章,我們繼續(xù)介紹可能出現(xiàn)的漏洞以及其中的原因,并就如何基于LazyHTML的思想構(gòu)建新的流量重寫(xiě)器。
特別聲明:以上文章內(nèi)容僅代表作者本人觀點(diǎn),不代表ESG跨境電商觀點(diǎn)或立場(chǎng)。如有關(guān)于作品內(nèi)容、版權(quán)或其它問(wèn)題請(qǐng)于作品發(fā)表后的30日內(nèi)與ESG跨境電商聯(lián)系。
二維碼加載中...
使用微信掃一掃登錄
使用賬號(hào)密碼登錄
平臺(tái)顧問(wèn)
微信掃一掃
馬上聯(lián)系在線顧問(wèn)
小程序
ESG跨境小程序
手機(jī)入駐更便捷
返回頂部