API查詢語(yǔ)言GraphQL的優(yōu)秀安全實(shí)踐
譯文【51CTO.com快譯】作為最為流行的查詢語(yǔ)言之一,GraphQL雖然能夠支持創(chuàng)建靈活的API,但是它也容易放過(guò)、甚至給應(yīng)用程序服務(wù)器帶來(lái)各種惡意的查詢。一些常見(jiàn)的GraphQL漏洞往往會(huì)在一致性評(píng)估和缺陷緩解等方面埋下各種安全隱患。在本文中,我們將深入和您探討GraphQL的各種常見(jiàn)漏洞,以及降低此類風(fēng)險(xiǎn)的優(yōu)秀實(shí)踐。
什么是GraphQL?
作為一種服務(wù)器端運(yùn)行時(shí)(runtime)的API查詢語(yǔ)言,GraphQL能夠優(yōu)先返回客戶端請(qǐng)求的數(shù)據(jù)。該語(yǔ)言不但能夠讓API變得輕巧、靈活,而且對(duì)于開發(fā)人員十分友好且方便他們進(jìn)行快速開發(fā)。而作為REST API框架的替代方案,GraphQL允許開發(fā)團(tuán)隊(duì)在單個(gè)接口的調(diào)用中,創(chuàng)建可訪問(wèn)來(lái)自多個(gè)數(shù)據(jù)源的請(qǐng)求。該語(yǔ)言可以被部署到集成開發(fā)環(huán)境(IDE)中,并提供描述用戶該如何請(qǐng)求數(shù)據(jù)的語(yǔ)法。可以說(shuō),GraphQL既提供了一個(gè)可預(yù)測(cè)運(yùn)行的框架,又允許開發(fā)人員自行選擇構(gòu)建API的方法。
GraphQL的常見(jiàn)安全問(wèn)答
API攻擊普遍嗎?
隨著API普遍被使用,針對(duì)它的攻擊嘗試在數(shù)量上也在持續(xù)增加。這些攻擊通常依賴于通過(guò)應(yīng)用程序的編程接口,去自動(dòng)化執(zhí)行各種惡意操作。根據(jù)Wallarm.com的一份統(tǒng)計(jì)報(bào)告,截至2019年,惡意自動(dòng)化攻擊主機(jī)、及其網(wǎng)絡(luò)已占互聯(lián)網(wǎng)的20%。這使得保護(hù)API成為應(yīng)用程序安全措施的一個(gè)關(guān)鍵環(huán)節(jié)。
是否有簡(jiǎn)化GraphQL安全性的工具?
目前,業(yè)界有多種開源的項(xiàng)目,可以簡(jiǎn)化GraphQL API的創(chuàng)建和管理。其中包括:Apollo client、Offix、Graphback和 OpenAPI-to-GraphQL。
GraphQL如何處理身份驗(yàn)證和授權(quán)?
由于GraphQL是一種服務(wù)器端運(yùn)行時(shí)查詢語(yǔ)言,因此它并不處理授權(quán)的邏輯。不過(guò),該平臺(tái)允許開發(fā)人員,在向客戶端公開API之前,在業(yè)務(wù)邏輯層中實(shí)施身份驗(yàn)證和授權(quán)的檢查。
GraphQL的具體安全性問(wèn)題
憑借著其豐富的平臺(tái)功能,以及能夠簡(jiǎn)化API查詢的創(chuàng)建過(guò)程,GraphQL已被譽(yù)為現(xiàn)代應(yīng)用開發(fā)技術(shù)棧的關(guān)鍵組件。而為了協(xié)助企業(yè)減少GraphQL API的攻擊面,我們可以通過(guò)如下方面來(lái)管控GraphQL平臺(tái)的固有安全問(wèn)題:
自定義標(biāo)量的驗(yàn)證不足
在使用GraphQL時(shí),原始數(shù)據(jù)往往是由標(biāo)量類型(scalar type)表示的。GraphQL API通常支持五種基本的標(biāo)量數(shù)據(jù)類型,即:Int、Float、Boolean、ID和String。雖然這個(gè)基本集合對(duì)于簡(jiǎn)單的API來(lái)說(shuō)已經(jīng)足夠了,但是GraphQL也允許開發(fā)人員針對(duì)特殊的原始數(shù)據(jù)API類型需求,自行創(chuàng)建標(biāo)量類型。當(dāng)然,開發(fā)人員需要針對(duì)此類配置,額外地增加用戶輸入的驗(yàn)證、以及清理的過(guò)程。相反,如果未能實(shí)現(xiàn)此類功能,則會(huì)危及到GraphQL標(biāo)量類型的安全性。
REST代理充當(dāng)API攻擊媒介
在調(diào)整現(xiàn)有的API以供GraphQL客戶端調(diào)用時(shí),開發(fā)人員通常會(huì)將GraphQL實(shí)現(xiàn)為,在內(nèi)部REST框架之上的一個(gè)瘦代理層。如果在沒(méi)有充分考慮到安全因素的情況下實(shí)施此類轉(zhuǎn)換,那么惡意用戶就可以任意修改API請(qǐng)求中所指定的路徑或參數(shù)。而修改后的請(qǐng)求在解析到后端API時(shí),攻擊者便可以實(shí)施跨站請(qǐng)求偽造( cross-site request forgery,CSRF)了。
授權(quán)缺陷
GraphQL將配置授權(quán)和身份驗(yàn)證的檢查責(zé)任,留給了最終實(shí)現(xiàn)者。即,GraphQL API在查詢級(jí)別的解析器、以及加載附加數(shù)據(jù)的解析器中,需要包含多項(xiàng)授權(quán)檢查。而當(dāng)授權(quán)由查詢級(jí)解析器直接處理時(shí),任何未經(jīng)檢查的API實(shí)例都會(huì)暴露受攻擊面。而且,隨著API模式復(fù)雜性的增加,此類被攻擊者利用的漏洞風(fēng)險(xiǎn)也會(huì)隨之增加。
自省查詢(Introspection Queries)可能會(huì)暴露敏感數(shù)據(jù)
開發(fā)人員為了去實(shí)現(xiàn)那些無(wú)法公開訪問(wèn)的“隱藏”API端點(diǎn),會(huì)啟用GraphQL服務(wù)器之間的API端點(diǎn)通信,或通過(guò)隱藏的管理功能來(lái)實(shí)現(xiàn)。其中,GraphQL包含了一個(gè)自省功能,可以在沒(méi)有適當(dāng)授權(quán)的情況下,輕松地訪問(wèn)各個(gè)端點(diǎn)。由于自省功能允許客戶端訪問(wèn)有關(guān)GraphQL架構(gòu)的信息,因此一旦有攻擊發(fā)生,自省查詢就可以被用于訪問(wèn)API的相關(guān)配置、以及其他客戶端的私有信息。
速率限制難以實(shí)施
從本質(zhì)上說(shuō),GraphQL API是比較復(fù)雜的。它的每一個(gè)查詢都會(huì)涉及到多項(xiàng)操作,并且會(huì)消耗大量的服務(wù)器資源。因此,光靠限制接收到的HTTP請(qǐng)求數(shù)量,并使用默認(rèn)的速率限制策略是不夠的。如果兩種對(duì)象類型之間存在著某種循環(huán)關(guān)系,那么攻擊者就可以通過(guò)創(chuàng)建各種濫用查詢(abusive queries),從而讓查詢本身變得異常復(fù)雜。以此產(chǎn)生的編排,能夠?qū)raphQL應(yīng)用發(fā)起拒絕服務(wù)式(DoS)的攻擊。
常見(jiàn)的GraphQL漏洞
下面,我們來(lái)進(jìn)一步討論GraphQL有哪些常見(jiàn)的漏洞,可被惡意攻擊者在API層面利用。
GraphQL批處理攻擊
GraphQL框架能夠支持自省查詢的批處理,即:能夠在一次性調(diào)用中,向后端API發(fā)送多個(gè)請(qǐng)求。由于減少了請(qǐng)求與服務(wù)器之間的往返次數(shù),因此這對(duì)于減少API請(qǐng)求的開銷非常實(shí)用。不過(guò),攻擊者也可以使用查詢的批處理功能,通過(guò)反復(fù)從API服務(wù)器、或數(shù)據(jù)庫(kù)處加載數(shù)據(jù),來(lái)編排各種快速且難以被檢測(cè)到的暴力攻擊。
以下典型示例展示了,在搜索數(shù)字記錄對(duì)象標(biāo)識(shí)(Digital Record Object Identification,DROID)對(duì)象的不同實(shí)例時(shí),進(jìn)行GraphQL批處理查詢的代碼:
- query {
- droid(id: "2000"){
- name
- }
- second:droid(id: "2001"){
- name
- }
- third:droid(id: "2002"){
- name
- }
- }
而攻擊者可以通過(guò)制造一些網(wǎng)絡(luò)請(qǐng)求,來(lái)枚舉API服務(wù)器中的每一個(gè)droid對(duì)象。這就可能會(huì)導(dǎo)致API級(jí)別的DoS攻擊、暴力破解秘密數(shù)據(jù)、繞過(guò)請(qǐng)求的速率限制、以及對(duì)象枚舉等安全問(wèn)題。
GraphQL注入攻擊
GraphQL API通常與作為數(shù)據(jù)源的數(shù)據(jù)庫(kù)管理系統(tǒng)相連接。API后端的Resolver在收到請(qǐng)求后,會(huì)根據(jù)操作集來(lái)區(qū)分查詢。在Resolver查詢數(shù)據(jù)庫(kù)時(shí),如果其操作涉及到數(shù)據(jù)的提取,那么就會(huì)直接執(zhí)行相應(yīng)的獲取操作??梢?jiàn),如果來(lái)自API客戶端的數(shù)據(jù),在未被適當(dāng)清理的情況下,執(zhí)行任何受信的操作,那么黑客就可以通過(guò)編排SQL/NoSQL,來(lái)實(shí)施注入攻擊。同樣,如果對(duì)輸入的清理不夠充分,攻擊者還會(huì)執(zhí)行諸如LDAP注入、以及命令注入等其他形式的注入攻擊。
GraphQL CSRF攻擊
跨站請(qǐng)求偽造(CSRF)攻擊是指,在合法用戶不知情時(shí),強(qiáng)迫Web服務(wù)器運(yùn)行那些不必要的操作。當(dāng)存在CSRF漏洞時(shí),攻擊者會(huì)在當(dāng)前登錄用戶的上下文,發(fā)送經(jīng)過(guò)身份驗(yàn)證的請(qǐng)求。而GraphQL類型的應(yīng)用極易受到CSRF攻擊,畢竟API在接收瀏覽器的請(qǐng)求時(shí),會(huì)自動(dòng)接受所有的cookie(其中就包括了會(huì)話cookie)。
目前,主要有兩種類型的GraphQL CSRF攻擊:基于Post和基于Get的CSRF。由于GraphQL使用多個(gè)API層來(lái)轉(zhuǎn)換傳入的多格式請(qǐng)求,而且能夠影響到GraphQL應(yīng)用的狀態(tài),因此大多數(shù)CSRF攻擊通常以POST請(qǐng)求為目標(biāo)。通常,許多開發(fā)人員會(huì)只接受設(shè)置為application/json的Content-Type標(biāo)頭。例如,以下POST請(qǐng)求可用于發(fā)出有效的GraphQL查詢:
- POST /GraphQLHTTP/1.1
- Host: redacted
- Connection: close
- Content-Length: 100
- accept: */*
- User-Agent: ...
- content-type: application/json
- Referer: https://redacted/
- Accept-Encoding: gzip, deflate
- Accept-Language: en-US,en;q=0.9
- Cookie: ...
- {"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
服務(wù)器可以將此請(qǐng)求作為form-urlencoded POST請(qǐng)求予以接受:
- POST /GraphQLHTTP/1.1
- Host: redacted
- Connection: close
- Content-Length: 72
- accept: */*
- User-Agent: Mozilla/5.0(Macintosh; Intel Mac OS X 11_2_2)AppleWebKit/537.36(KHTML, like Gecko)Chrome/89.0.4389.82 Safari/537.36
- Content-Type: application/x-www-form-urlencoded
- Referer: https://redacted
- Accept-Encoding: gzip, deflate
- Accept-Language: en-US,en;q=0.9
- Cookie: ...
- query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
而有經(jīng)驗(yàn)的攻擊者則可以使用自動(dòng)化掃描工具,將其轉(zhuǎn)換為CSRF的攻擊接口:
彌補(bǔ)GraphQL漏洞的清單
下面,我們將以清單的形式,給出一些與GraphQL安全有關(guān)的優(yōu)秀實(shí)踐。
防止GraphQL注入攻擊
對(duì)于那些由LDAP、ORMs/SQL/NoSQL或XML等輔助解析器,來(lái)處理輸入信息的應(yīng)用而言,我們建議開發(fā)人員做到如下方面:
- 選擇諸如參數(shù)化語(yǔ)句等能夠提供安全API的庫(kù)。
- 根據(jù)所選用的解析器的最佳實(shí)踐,對(duì)輸入進(jìn)行轉(zhuǎn)義或編碼。
- 遵循所選模塊的文檔,以正確的方式使用該工具。畢竟,大多數(shù)語(yǔ)言和框架都內(nèi)置了編碼/轉(zhuǎn)義功能,因此了解它們的核心功能,并選擇適合的用例是非常重要的。
預(yù)防DoS攻擊
DoS攻擊旨在使得GraphQL API變慢、甚至無(wú)法響應(yīng)正常的請(qǐng)求。為了防御此類攻擊,我們應(yīng)做到:
- 為傳入的GraphQL查詢實(shí)施深度的規(guī)則限制(depth limiting)。
- 為基礎(chǔ)設(shè)施和API層添加超時(shí)設(shè)定。
- 執(zhí)行查詢的成本分析,以限制代價(jià)昂貴的查詢。
- 對(duì)每個(gè)API客戶端的傳入請(qǐng)求,實(shí)施速率限制。
GraphQL API的訪問(wèn)控制
為了保護(hù)對(duì)GraphQL API的合理訪問(wèn),開發(fā)人員應(yīng)該做到:
- 驗(yàn)證當(dāng)前用戶是否有權(quán)根據(jù)他們的請(qǐng)求,查看、改變、以及修改數(shù)據(jù)。
- 對(duì)端點(diǎn)和邊緣實(shí)施授權(quán)控制。
- 利用基于角色的訪問(wèn)控制(RBAC)中間件,通過(guò)查詢和變異解析器(mutation solver),來(lái)啟用訪問(wèn)控制。
- 在公共的API中禁用自省查詢。
- 禁用GraphiQL之類針對(duì)GraphQL模式的探查工具。
通用GraphQL API安全實(shí)踐
開發(fā)人員還可以用來(lái)保護(hù)GraphQL層的其他方法包括:
- 對(duì)允許的字符使用白名單。
- 為突變的輸入預(yù)先定義好對(duì)應(yīng)的GraphQL模式。
- 使用單一的內(nèi)部字符編碼格式,來(lái)正確地處理Unicode輸入。
- 添加分頁(yè)(pagination),以限制單個(gè)請(qǐng)求能夠一次性訪問(wèn)到的信息量。
原文標(biāo)題:Best Practices For GraphQL Security,作者:Sudip Sengupta
【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】
































