業(yè)數(shù)字化爆火背后:餐飲小程序與儲(chǔ)值系統(tǒng)的安全漏洞及低成本防護(hù))
1. 從一只蝦到一條鏈爆火背后被忽視的攻擊面小龍蝦這個(gè)品類在過去兩年里經(jīng)歷了一輪罕見的爆發(fā)式增長(zhǎng)。從街邊大排檔到連鎖品牌從線下堂食到預(yù)制菜電商整條產(chǎn)業(yè)鏈在極短時(shí)間內(nèi)完成了數(shù)字化改造。點(diǎn)單小程序、會(huì)員系統(tǒng)、供應(yīng)鏈管理平臺(tái)、直播帶貨后臺(tái)、外賣聚合接單系統(tǒng)——這些工具在半年內(nèi)被倉促塞進(jìn)了原本只靠一部電話和一本手寫賬本就能運(yùn)轉(zhuǎn)的生意里。我之所以關(guān)注這個(gè)話題是因?yàn)樯磉吅脦讉€(gè)做餐飲SaaS的朋友都在聊同一件事客戶催著上線安全評(píng)審基本為零。一個(gè)典型的場(chǎng)景是某連鎖品牌為了趕在旺季前上線會(huì)員儲(chǔ)值功能從立項(xiàng)到上線只用了十一天。十一天里前端調(diào)通了支付接上了數(shù)據(jù)庫建好了但沒有人問過一句這個(gè)系統(tǒng)的接口有沒有做頻率限制用戶手機(jī)號(hào)在數(shù)據(jù)庫里是明文還是加密后臺(tái)管理頁面的登錄態(tài)是怎么維持的這不是個(gè)別現(xiàn)象。當(dāng)一個(gè)行業(yè)突然涌入大量熱錢和流量時(shí)技術(shù)團(tuán)隊(duì)的第一優(yōu)先級(jí)永遠(yuǎn)是能不能跑起來而不是跑起來之后會(huì)不會(huì)被人從后面踹一腳。小龍蝦產(chǎn)業(yè)的數(shù)字化恰恰處在這個(gè)階段——業(yè)務(wù)跑得比安全快而攻擊者最喜歡的就是這種時(shí)間差。這篇文章想聊的不是某個(gè)具體漏洞的利用教程而是從從業(yè)者視角出發(fā)把這類爆火行業(yè)數(shù)字化中反復(fù)出現(xiàn)的幾類安全問題拆開來看。我會(huì)講清楚每一類問題的成因、攻擊者通常從哪個(gè)角度切入、以及在實(shí)際項(xiàng)目中可以怎么低成本地堵住這些口子。適合正在做餐飲/零售類系統(tǒng)的開發(fā)者、剛接手這類項(xiàng)目的技術(shù)負(fù)責(zé)人以及對(duì)業(yè)務(wù)安全感興趣的讀者參考。2. 點(diǎn)單小程序里的數(shù)據(jù)暴露從手機(jī)號(hào)到儲(chǔ)值余額2.1 為什么這類系統(tǒng)天生容易泄露數(shù)據(jù)餐飲類小程序的開發(fā)節(jié)奏和傳統(tǒng)企業(yè)軟件完全不同。傳統(tǒng)企業(yè)軟件有明確的需求文檔、測(cè)試周期和上線評(píng)審而餐飲小程序往往是老板今天說要加個(gè)功能明天就要看到效果。這種節(jié)奏下開發(fā)者的注意力全部集中在功能實(shí)現(xiàn)上數(shù)據(jù)安全往往被默認(rèn)成框架會(huì)幫我處理。但框架不會(huì)幫你處理業(yè)務(wù)層面的數(shù)據(jù)暴露。舉個(gè)最常見的例子用戶在小程序里查看自己的訂單詳情前端發(fā)起一個(gè)請(qǐng)求后端返回一個(gè)包含訂單信息的JSON。如果這個(gè)接口的設(shè)計(jì)是傳入訂單ID返回訂單詳情而沒有校驗(yàn)這個(gè)訂單ID是否屬于當(dāng)前登錄用戶那么任何人只要把訂單ID改一個(gè)數(shù)字就能看到別人的訂單——包括姓名、手機(jī)號(hào)、配送地址、消費(fèi)金額。這類問題在安全圈里叫越權(quán)訪問分為水平越權(quán)和垂直越權(quán)兩種。水平越權(quán)是同級(jí)用戶之間的數(shù)據(jù)互訪比如A用戶能看到B用戶的訂單垂直越權(quán)是低權(quán)限用戶訪問高權(quán)限功能比如普通用戶能進(jìn)管理后臺(tái)。餐飲類系統(tǒng)里水平越權(quán)是最常見的因?yàn)橛唵尾樵?、?huì)員信息查詢、優(yōu)惠券領(lǐng)取這些功能天然就是按ID取數(shù)據(jù)的模式。2.2 一個(gè)真實(shí)的排查思路假設(shè)你接手了一個(gè)已經(jīng)上線的點(diǎn)單系統(tǒng)想快速判斷它有沒有越權(quán)問題可以按下面的順序過一遍列出所有傳入ID返回?cái)?shù)據(jù)的接口。打開瀏覽器的開發(fā)者工具在小程序里把每個(gè)頁面都點(diǎn)一遍記錄下所有請(qǐng)求URL和參數(shù)。重點(diǎn)關(guān)注包含id、orderId、userId、memberId這類參數(shù)的接口。用兩個(gè)賬號(hào)做交叉測(cè)試。注冊(cè)兩個(gè)普通賬號(hào)分別下單然后用賬號(hào)A的登錄態(tài)去請(qǐng)求賬號(hào)B的訂單ID。如果返回了數(shù)據(jù)說明存在水平越權(quán)。檢查返回字段。即使接口做了歸屬校驗(yàn)也要看返回的字段里有沒有多余信息。比如訂單詳情接口返回了用戶的完整手機(jī)號(hào)但前端只展示了后四位——那這個(gè)完整手機(jī)號(hào)就是多余的暴露面。檢查批量接口。有些系統(tǒng)為了減少請(qǐng)求次數(shù)會(huì)提供批量查詢接口比如傳入一個(gè)ID數(shù)組返回多條數(shù)據(jù)。這類接口如果沒做逐條歸屬校驗(yàn)危害比單條查詢更大。注意做這類測(cè)試時(shí)務(wù)必使用自己注冊(cè)的測(cè)試賬號(hào)不要嘗試訪問真實(shí)用戶的數(shù)據(jù)。未經(jīng)授權(quán)的測(cè)試可能觸犯法律。2.3 修復(fù)方案與成本評(píng)估越權(quán)問題的修復(fù)其實(shí)不復(fù)雜核心原則就一條每一個(gè)數(shù)據(jù)查詢都必須帶上當(dāng)前登錄用戶的身份條件。具體到代碼層面就是把WHERE id ?改成WHERE id ? AND user_id ?。如果用的是ORM框架就是在查詢條件里強(qiáng)制附加當(dāng)前用戶的ID。但實(shí)際項(xiàng)目中這個(gè)修復(fù)往往會(huì)遇到阻力。原因在于有些接口在設(shè)計(jì)之初就沒有考慮用戶維度比如根據(jù)訂單號(hào)查詢訂單這個(gè)功能可能是給客服用的客服需要能查任何訂單。這種情況下正確的做法是把接口拆成兩個(gè)面向用戶的接口強(qiáng)制帶用戶ID面向客服的接口走獨(dú)立的權(quán)限校驗(yàn)。成本方面一個(gè)中等規(guī)模的餐飲小程序大概有30到50個(gè)接口其中涉及數(shù)據(jù)查詢的可能有20個(gè)左右。如果代碼結(jié)構(gòu)清晰一個(gè)熟練的開發(fā)者大概需要兩到三天完成排查和修復(fù)。如果代碼是外包做的、結(jié)構(gòu)混亂時(shí)間可能要翻倍。但相比數(shù)據(jù)泄露后的公關(guān)成本和監(jiān)管處罰這個(gè)投入是值得的。3. 儲(chǔ)值余額與優(yōu)惠券黑產(chǎn)眼中的高價(jià)值目標(biāo)3.1 為什么儲(chǔ)值功能是重災(zāi)區(qū)小龍蝦品類的客單價(jià)不低很多品牌會(huì)推儲(chǔ)值優(yōu)惠來鎖定復(fù)購。比如充500送100用戶賬戶里就有了600元的余額。這個(gè)余額在系統(tǒng)里就是一個(gè)數(shù)字字段而這個(gè)數(shù)字字段的讀寫邏輯往往就是黑產(chǎn)重點(diǎn)關(guān)照的對(duì)象。我見過最離譜的一個(gè)案例是某品牌的儲(chǔ)值系統(tǒng)在充值回調(diào)接口里沒有做簽名校驗(yàn)。也就是說支付平臺(tái)通知用戶充值成功的那個(gè)請(qǐng)求任何人都可以偽造。攻擊者只需要構(gòu)造一個(gè)包含用戶ID和充值金額的請(qǐng)求發(fā)給服務(wù)器賬戶余額就直接加上去了。這個(gè)漏洞從上線到被發(fā)現(xiàn)中間隔了將近兩個(gè)月?lián)p失的具體金額沒有對(duì)外公布但據(jù)說足夠讓技術(shù)負(fù)責(zé)人直接走人。即使做了簽名校驗(yàn)還有一類問題叫并發(fā)競(jìng)爭(zhēng)。比如用戶賬戶里有100元余額同時(shí)發(fā)起兩筆80元的消費(fèi)請(qǐng)求。如果系統(tǒng)在處理時(shí)是先讀余額、判斷夠不夠、再扣減那么兩個(gè)請(qǐng)求可能同時(shí)讀到100元都判斷為夠然后各自扣減80元最終余額變成-60元。這就是典型的競(jìng)態(tài)條件漏洞。3.2 支付回調(diào)的安全設(shè)計(jì)要點(diǎn)支付回調(diào)是儲(chǔ)值系統(tǒng)的命門設(shè)計(jì)時(shí)至少要滿足以下幾點(diǎn)簽名驗(yàn)證支付平臺(tái)的回調(diào)請(qǐng)求必須攜帶簽名服務(wù)端要用約定的密鑰驗(yàn)證簽名。簽名不通過的請(qǐng)求直接丟棄不要返回任何業(yè)務(wù)信息。金額校驗(yàn)回調(diào)里的金額必須和訂單創(chuàng)建時(shí)的金額一致。有些系統(tǒng)只校驗(yàn)訂單號(hào)不校驗(yàn)金額攻擊者可以把1元充值的回調(diào)改成1000元。冪等處理同一個(gè)回調(diào)可能被重復(fù)發(fā)送系統(tǒng)必須保證同一筆訂單只處理一次。通常的做法是在數(shù)據(jù)庫里記錄已處理的回調(diào)ID處理前先查重。異步確認(rèn)不要依賴前端跳轉(zhuǎn)結(jié)果來判定支付成功必須以服務(wù)端收到的回調(diào)為準(zhǔn)。前端跳轉(zhuǎn)可以被偽造服務(wù)端回調(diào)相對(duì)可靠。3.3 優(yōu)惠券系統(tǒng)的常見邏輯漏洞優(yōu)惠券的問題通常不在技術(shù)層面而在業(yè)務(wù)邏輯層面。比如無限領(lǐng)取領(lǐng)券接口沒有做頻率限制也沒有校驗(yàn)用戶是否已領(lǐng)過。攻擊者寫個(gè)腳本就能把一批券全部領(lǐng)走。疊加使用系統(tǒng)允許同一訂單使用多張優(yōu)惠券但沒有限制疊加規(guī)則。攻擊者通過組合不同券種可以把訂單金額壓到極低甚至負(fù)數(shù)。過期券復(fù)用券過期后狀態(tài)沒有及時(shí)更新或者更新了但核銷接口沒有校驗(yàn)狀態(tài)導(dǎo)致過期券仍可使用。退款不退券用戶用券下單后申請(qǐng)退款系統(tǒng)退了錢但沒有收回優(yōu)惠券用戶相當(dāng)于白賺一張券。這些問題的共同點(diǎn)是它們不是代碼寫錯(cuò)了而是業(yè)務(wù)規(guī)則沒有想清楚。修復(fù)方式通常是在關(guān)鍵操作上加校驗(yàn)、加限制、加日志。比如領(lǐng)券接口加用戶維度的頻率限制核銷接口加券狀態(tài)和有效期的雙重校驗(yàn)退款流程里加券的回收邏輯。4. 供應(yīng)鏈與后臺(tái)系統(tǒng)被低估的入口4.1 后臺(tái)管理頁面的弱口令問題前臺(tái)小程序的安全做得再好如果后臺(tái)管理頁面用的是admin/123456那一切努力都白費(fèi)。餐飲行業(yè)的后臺(tái)系統(tǒng)有個(gè)特點(diǎn)使用者往往是不懂技術(shù)的店長(zhǎng)或運(yùn)營(yíng)人員他們傾向于設(shè)置簡(jiǎn)單好記的密碼而且多個(gè)系統(tǒng)共用同一個(gè)密碼。更麻煩的是很多后臺(tái)系統(tǒng)的登錄頁面直接暴露在公網(wǎng)上沒有做IP白名單也沒有做登錄失敗鎖定。攻擊者可以用自動(dòng)化工具對(duì)后臺(tái)登錄接口進(jìn)行撞庫成功率遠(yuǎn)比想象中高。我建議的做法是后臺(tái)系統(tǒng)盡量不直接暴露在公網(wǎng)通過內(nèi)網(wǎng)訪問或加一層訪問控制。強(qiáng)制密碼復(fù)雜度至少8位包含大小寫字母和數(shù)字。登錄失敗5次后鎖定賬戶15分鐘或者引入圖形驗(yàn)證碼。對(duì)后臺(tái)的所有操作記錄審計(jì)日志包括登錄、查詢、修改、導(dǎo)出。4.2 供應(yīng)鏈數(shù)據(jù)的敏感性小龍蝦供應(yīng)鏈涉及采購價(jià)格、供應(yīng)商信息、庫存數(shù)量、物流軌跡等數(shù)據(jù)。這些數(shù)據(jù)在競(jìng)爭(zhēng)激烈的餐飲行業(yè)里屬于核心商業(yè)機(jī)密。如果被競(jìng)爭(zhēng)對(duì)手拿到可以直接推算出你的成本結(jié)構(gòu)和采購渠道。供應(yīng)鏈系統(tǒng)的安全重點(diǎn)和前臺(tái)不同它更關(guān)注訪問控制和數(shù)據(jù)脫敏。比如采購價(jià)格字段只對(duì)采購角色可見運(yùn)營(yíng)角色只能看到庫存數(shù)量。供應(yīng)商聯(lián)系方式在非必要場(chǎng)景下脫敏展示。數(shù)據(jù)導(dǎo)出功能加審批流程防止內(nèi)部人員批量導(dǎo)出后帶走。這些措施聽起來簡(jiǎn)單但在實(shí)際項(xiàng)目中往往被忽略因?yàn)橄到y(tǒng)上線時(shí)通常只考慮了功能能不能用沒有考慮誰能用哪些功能。4.3 第三方接口的信任邊界餐飲系統(tǒng)通常會(huì)對(duì)接多個(gè)第三方服務(wù)支付、短信、地圖、外賣平臺(tái)、電子發(fā)票等。每對(duì)接一個(gè)第三方就多一個(gè)外部入口。如果第三方接口的調(diào)用沒有做鑒權(quán)或者回調(diào)地址暴露在外就可能被利用。一個(gè)常見的疏忽是短信接口沒有做頻率限制。攻擊者發(fā)現(xiàn)你的短信發(fā)送接口后可以寫腳本瘋狂調(diào)用導(dǎo)致你的短信費(fèi)用暴漲同時(shí)用戶被騷擾。修復(fù)方式是在發(fā)送短信前加圖形驗(yàn)證碼并對(duì)同一手機(jī)號(hào)、同一IP做頻率限制。另一個(gè)疏忽是外賣平臺(tái)的訂單回調(diào)沒有做來源校驗(yàn)。攻擊者可以偽造訂單回調(diào)在你的系統(tǒng)里生成虛假訂單干擾正常運(yùn)營(yíng)。修復(fù)方式是校驗(yàn)回調(diào)請(qǐng)求的簽名或來源IP。5. 從應(yīng)急到常態(tài)小團(tuán)隊(duì)的安全建設(shè)路徑5.1 先做資產(chǎn)梳理再談防護(hù)很多小團(tuán)隊(duì)一提到安全建設(shè)第一反應(yīng)是買個(gè)WAF或者上個(gè)堡壘機(jī)。但在不知道自己要保護(hù)什么的情況下買再多設(shè)備也是浪費(fèi)。第一步應(yīng)該是資產(chǎn)梳理把所有的域名、服務(wù)器、數(shù)據(jù)庫、接口、第三方服務(wù)都列出來標(biāo)注每個(gè)資產(chǎn)的重要程度和負(fù)責(zé)人。資產(chǎn)梳理的產(chǎn)出是一張表大概長(zhǎng)這樣資產(chǎn)類型具體資產(chǎn)重要程度負(fù)責(zé)人當(dāng)前防護(hù)措施域名小程序API域名高后端A無服務(wù)器生產(chǎn)環(huán)境應(yīng)用服務(wù)器高運(yùn)維B基礎(chǔ)防火墻數(shù)據(jù)庫用戶數(shù)據(jù)庫高后端A無審計(jì)接口支付回調(diào)接口高后端A簽名校驗(yàn)第三方短信服務(wù)中后端C無頻率限制這張表不需要很精確但必須要有。有了它你才能判斷哪些地方需要優(yōu)先投入。5.2 用最低成本堵住最大的洞對(duì)于資源有限的小團(tuán)隊(duì)安全建設(shè)應(yīng)該遵循二八原則用20%的投入解決80%的風(fēng)險(xiǎn)。根據(jù)我的經(jīng)驗(yàn)以下幾項(xiàng)是性價(jià)比最高的接口鑒權(quán)全覆蓋確保每一個(gè)需要登錄才能訪問的接口都做了身份校驗(yàn)每一個(gè)數(shù)據(jù)查詢都帶了用戶歸屬條件。這一項(xiàng)能擋住大部分越權(quán)問題。關(guān)鍵操作加日志登錄、支付、退款、修改密碼、導(dǎo)出數(shù)據(jù)這些操作必須記錄日志包括操作人、時(shí)間、IP、操作內(nèi)容。出問題時(shí)日志是唯一的追溯依據(jù)。數(shù)據(jù)庫定期備份備份要離線保存并且定期驗(yàn)證可恢復(fù)性。很多團(tuán)隊(duì)做了備份但從來沒恢復(fù)過真出事時(shí)才發(fā)現(xiàn)備份是壞的。依賴組件及時(shí)更新框架、中間件、第三方庫的已知漏洞是攻擊者的首選目標(biāo)。建立一個(gè)簡(jiǎn)單的依賴清單定期檢查更新。5.3 把安全寫進(jìn)開發(fā)流程安全問題的根源往往不在技術(shù)而在流程。如果每次上線都是先上再說那安全問題永遠(yuǎn)只能靠事后補(bǔ)救。小團(tuán)隊(duì)可以嘗試把以下幾件事寫進(jìn)開發(fā)流程上線前檢查清單包括接口鑒權(quán)、敏感數(shù)據(jù)加密、日志記錄、錯(cuò)誤信息脫敏等。不需要很復(fù)雜一頁紙就夠。代碼評(píng)審關(guān)注安全在代碼評(píng)審時(shí)除了看邏輯對(duì)不對(duì)也看一眼有沒有明顯的安全問題比如SQL拼接、硬編碼密鑰、日志打印敏感信息。定期做一次簡(jiǎn)單掃描用開源的掃描工具對(duì)線上系統(tǒng)做一次基礎(chǔ)掃描看看有沒有暴露的敏感文件、弱口令、已知漏洞。頻率不用很高一個(gè)季度一次就行。這些措施不會(huì)讓你的系統(tǒng)變得絕對(duì)安全但能讓攻擊者的成本顯著提高。在安全領(lǐng)域讓攻擊者覺得不劃算往往比追求無懈可擊更現(xiàn)實(shí)。6. 一個(gè)值得反復(fù)提醒的細(xì)節(jié)日志里的敏感信息最后聊一個(gè)特別容易被忽略、但危害很大的問題日志泄露。開發(fā)者在排查問題時(shí)習(xí)慣把請(qǐng)求參數(shù)和響應(yīng)內(nèi)容都打到日志里。這本身沒問題問題在于很多系統(tǒng)會(huì)把用戶的手機(jī)號(hào)、身份證號(hào)、銀行卡號(hào)、密碼明文打進(jìn)日志。而這些日志往往存儲(chǔ)在多個(gè)地方應(yīng)用服務(wù)器本地、日志收集系統(tǒng)、監(jiān)控平臺(tái)、甚至開發(fā)者的本地電腦。一旦這些日志被泄露危害不亞于數(shù)據(jù)庫被拖。而且日志泄露往往更隱蔽因?yàn)楹芏嗳烁静恢雷约旱娜罩纠镉惺裁?。我的建議是在日志框架里配置脫敏規(guī)則對(duì)手機(jī)號(hào)、身份證號(hào)、銀行卡號(hào)等字段自動(dòng)打碼。禁止在日志里打印密碼、密鑰、Token等憑證信息。日志的訪問權(quán)限要控制不是所有開發(fā)人員都需要看生產(chǎn)日志。日志的保留時(shí)間要有策略不需要永久保存的日志定期清理。這些操作不需要太多技術(shù)投入但能堵住一個(gè)非常隱蔽的泄露渠道。我在實(shí)際項(xiàng)目中見過太多數(shù)據(jù)庫加密做得很好但日志里全是明文手機(jī)號(hào)的情況希望你不要成為下一個(gè)。