一個 App 上架之後,會被裝進成千上萬支你看不到、也管不到的手機。這一關先不碰任何平台的 API,只回答一個問題:伺服器憑什麼相信「送請求來的,真的是我的正版 App」?
後面三站(Apple 的做法、Google 的做法、伺服器怎麼統一驗)都建立在這關的觀念上,所以這關是地基。
這關學完,你要能用自己的話回答這 5 件事:
① 為什麼伺服器不能相信 App 自己說「我很安全」?
② 「設備證明」是誰、替誰、擔保什麼?
③ 數位簽章、憑證鏈、硬體保護的金鑰,各自負責什麼?
④ challenge 怎麼擋住「重送」?
⑤ 證明能做到什麼、做不到什麼?
下面這幾個詞,第一段馬上會用到。先掃一眼就好,不用背。
| 名詞 | 白話 |
|---|---|
| client(客戶端) | 使用者手上的 App。對面是你自己管的伺服器。 |
| API | 伺服器開給 App 呼叫的窗口,例如「兌換優惠券」的窗口。 |
| 請求(request) | App 透過 API 送給伺服器的一則訊息,例如「幫我兌換這張優惠券」。 |
| 重新打包(repackaging) | 把 App 的安裝檔拆開、改掉程式,再重新簽名、包回一個「長得一樣」的 App。 |
| hook(掛鉤) | App 執行到一半時,攔下它呼叫的某個函式,換成攻擊者想要的結果。 |
| root/越獄 | 拿到手機作業系統的最高權限。Android 叫 root,iPhone 叫越獄(jailbreak)。系統原本的保護,這時都能被關掉。 |
| 模擬器(emulator) | 在電腦上「假裝」成一支手機的軟體。 |
| 重送(replay) | 把一則以前的合法請求,原封不動再送一次。 |
想像你有一支兌換優惠券的 API /coupons/redeem。App 送請求時順便說:「我是正版,這支手機也沒被 root。」
問題是:這句話是 App 自己說的。攻擊者至少有六種方法讓它說謊,或乾脆不需要它:
圖 1:六種路線送來的請求,伺服器光看內容很難分辨真假(點圖可放大)
Apple 官方文件說得很直白:你不能靠 App 自己的邏輯替自己做安全檢查,因為被入侵的 App 可以偽造結果。OWASP 的行動 App 安全標準(MASVS)則把這類手機端的防護(韌性控制)定位成額外的保護:少了它本身不算漏洞,App 仍要依自己的威脅模型滿足其他所有控制項。
一句話:從 client 送來的所有東西,包括「我很安全」這句話,都可能是假的。我們需要一個攻擊者造不出來的東西。
比喻:網購時,賣家自己寫「保證正品」沒什麼用。但如果附上一張原廠開的、有防偽印章的保證書,你就能自己檢查真假。
設備證明(attestation)就是那張保證書:請作業系統/平台出具一份證明,內容是「這是某某正版 App,跑在一支真的裝置上」。平台背後有硬體保護的金鑰撐腰(下面會講),所以攻擊者很難偽造。
整件事有三個角色:
重點:判定一定在伺服器端做,App 只負責把東西帶過去。App 會向平台拿一份它自己改不了的證明(Apple 用簽章、Google 用加密),交給你的伺服器。Apple 那份由你的伺服器自己驗;Google 那份由你的伺服器送回 Google 解開、拿回判定結果。理由都一樣:手機上的 App 可能已經被改過,所以不能讓它說了算。
Apple 和 Google 各有一套實作,後面兩站再講。
一句話:設備證明=平台替 App 開的保證書;App 只負責轉交,伺服器負責驗。
保證書之所以可信,靠的是三個觀念。一次講一塊:
比喻:印章+印鑑證明。一組金鑰分成兩半:
簽章:主人用私鑰替一段資料算出一個「章」。驗證:別人用公鑰檢查這個章。對得上,就代表兩件事:這段資料確實出自私鑰主人,而且一個字都沒被改過。
小例子:資料是 app=com.example.shop,附上簽章。攻擊者把它改成 app=com.evil.shop,簽章就對不上了,驗證失敗。
那攻擊者重新打包後,自己再簽一次不就好了?可以簽,但他只能用自己的私鑰,蓋出來的章跟原廠的不一樣。所以簽章擋的不是「有沒有簽」,而是「是不是對的人簽的」。
但還有一個問題:伺服器怎麼知道這把公鑰真的屬於「平台認可的真裝置」,而不是攻擊者自己生的?
答案是憑證(certificate):一張寫著「這把公鑰屬於誰」的文件,由上一層的單位蓋章擔保。上一層又被更上一層擔保,一路到最頂端的根憑證(root certificate)。中間那幾層就叫中間憑證,整串合起來叫憑證鏈。
比喻:行員的章由分行主管蓋章擔保,分行主管的章再由總行蓋章擔保;你只要認得總行的章,就能一路信到行員的章。
圖 2:上面的線=誰替誰擔保;下面的線=伺服器驗證的方向(點圖可放大)
誰來驗這條鏈,看平台:Apple 的做法要你的伺服器自己驗,一路接到 Apple 的根;也有平台是由平台伺服器替你驗,你只拿回結果(Level 2 再講)。
一句話:簽章證明「資料出自這把私鑰、沒被改」;憑證鏈證明「這把私鑰是平台認可的」。
問題:如果私鑰就放在手機的一般儲存空間,root 過的手機可以直接把它複製走,拿去別的地方簽。
解法:手機裡有一塊跟主系統隔開的安全區。私鑰在裡面產生,永遠不出來;App 只能把資料遞進去,請它簽好再拿回來。
比喻:櫃檯後面上鎖的蓋章機。你可以遞紙進去請它蓋章,但沒辦法把印章拿走。
不同平台的名字不一樣,概念相同:
要記住一個細節:拿不走,不代表不能用。Android 文件說得很清楚:App 被攻破時,攻擊者偷不到金鑰,但仍然可以在那支手機上使用它。這點在「做不到什麼」會再用到。
兩家用法不一樣:
一句話:私鑰在安全硬體裡產生、永遠不離開,所以簽章只可能來自那一支實體裝置。
問題:攻擊者從一支真手機拿到一張合法證明,然後在模擬器上一直重複附上它。簽章是真的、憑證鏈也是真的,前兩塊積木都擋不住。
解法:伺服器先發一個 challenge(也常叫 nonce,意思是「只用一次的數字」):一串隨機、猜不到的值。平台產生證明時,要把它一起簽進去。
伺服器收到證明後,檢查三件事:
兩家做法不太一樣:
clientDataHash)。nonce,由你的伺服器發一次性的值;新的一種叫 requestHash,由平台自動防重送,你只要把「這次請求的內容」做成摘要綁進證明裡,並檢查時間戳(太舊就不收)。Google 也提醒不要把證明結果快取起來重複用。上面「三件事」是自己發 challenge 時的檢查法;平台代管時,同樣的事由平台替你做掉一部分。
一句話:challenge 讓證明綁定「這一次」,錄下來再送也沒用。
把三塊積木放回流程裡,就是下面這張圖。先看線怎麼流,再看下面的步驟說明。
圖 3:通用流程。有的平台不用你發 challenge、改由平台防重送;但「平台作證 → 伺服器端驗 → 你決定」的骨架都一樣(點圖可放大)
一句話:伺服器出一張一次性的單子 → 平台在保險箱裡蓋章 → 伺服器自己驗章。
先講結論:證明是一個很強的「風險訊號」,但不是百分之百的保證。
| ✅ 做得到 | ⚠️ 做不到/要小心 |
|---|---|
| 讓改過的 App、模擬器、腳本很難拿到有效證明,大幅墊高攻擊成本。 | 不是 100%。Apple 自己承認:改了作業系統的攻擊者可能繞過限制,也無法絕對確定地偵測詐騙。 |
| 給伺服器一個攻擊者很難偽造的判斷依據。 | 一支被攻破的真手機,可能替很多假請求出證明(呼應「拿不走,但能用」)。 |
| challenge 讓錄下來重送失效。 | 不能單獨使用。Google 說它最好搭配其他訊號,不該是唯一的防濫用機制;OWASP 也把這類保護定位為額外的保護,App 仍要依威脅模型滿足其他控制項(例如登入驗證)。 |
所以「沒拿到證明」不等於「一定是壞人」。失敗時要怎麼處理,Level 3 再談。
一句話:證明能大幅降低風險,但會誤判、有隱私成本、也不是萬能。它是「加分的訊號」,不是「唯一的門」。
能不看筆記回答下面 5 題,這關就過了。
Level 1 會拆開 Apple 的實作,看這套骨架在 iPhone 上長什麼樣。