設備證明 Level 0:伺服器為什麼不能相信 App(威脅模型與基本觀念)

Chance Lu · 學習文章 · 2026-10-22 15:29
Chance Lu

一個 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)把一則以前的合法請求,原封不動再送一次。

核心問題:App 在別人手上,它說的話不能全信

想像你有一支兌換優惠券的 API /coupons/redeem。App 送請求時順便說:「我是正版,這支手機也沒被 root。」

問題是:這句話是 App 自己說的。攻擊者至少有六種方法讓它說謊,或乾脆不需要它:

使用者的手機(你管不到) ① 重新打包:改過的 App拆開、刪掉檢查、重新簽名 ② hook:執行中被竄改把檢查結果換成「安全」 ③ root/越獄的手機系統的保護都能被關掉 ④ 模擬器:電腦假裝成手機一台電腦開幾百支 ⑤ 腳本/機器人沒有 App,直接打 API ⑥ 重送把錄下的舊請求再送一次 你的伺服器收到的請求看起來全都一樣分不出誰是真的

圖 1:六種路線送來的請求,伺服器光看內容很難分辨真假(點圖可放大)

Apple 官方文件說得很直白:你不能靠 App 自己的邏輯替自己做安全檢查,因為被入侵的 App 可以偽造結果。OWASP 的行動 App 安全標準(MASVS)則把這類手機端的防護(韌性控制)定位成額外的保護:少了它本身不算漏洞,App 仍要依自己的威脅模型滿足其他所有控制項。

一句話:從 client 送來的所有東西,包括「我很安全」這句話,都可能是假的。我們需要一個攻擊者造不出來的東西。

設備證明:請一個攻擊者造不出的「第三方」來擔保

比喻:網購時,賣家自己寫「保證正品」沒什麼用。但如果附上一張原廠開的、有防偽印章的保證書,你就能自己檢查真假。

設備證明(attestation)就是那張保證書:請作業系統/平台出具一份證明,內容是「這是某某正版 App,跑在一支真的裝置上」。平台背後有硬體保護的金鑰撐腰(下面會講),所以攻擊者很難偽造。

整件事有三個角色:

重點:判定一定在伺服器端做,App 只負責把東西帶過去。App 會向平台拿一份它自己改不了的證明(Apple 用簽章、Google 用加密),交給你的伺服器。Apple 那份由你的伺服器自己驗;Google 那份由你的伺服器送回 Google 解開、拿回判定結果。理由都一樣:手機上的 App 可能已經被改過,所以不能讓它說了算。

Apple 和 Google 各有一套實作,後面兩站再講。

一句話:設備證明=平台替 App 開的保證書;App 只負責轉交,伺服器負責驗。

為什麼攻擊者造不出這張保證書?靠三塊積木

保證書之所以可信,靠的是三個觀念。一次講一塊:

  1. 數位簽章(加上憑證鏈):只有特定的人蓋得出的章,而且查得到是誰擔保的。
  2. 硬體保護的金鑰:那顆章被鎖在手機的保險箱裡,偷不走。
  3. challenge:每張保證書只能用一次,擋住重送。

積木一:數位簽章與憑證鏈——只有它蓋得出來的章

比喻:印章+印鑑證明。一組金鑰分成兩半:

簽章:主人用私鑰替一段資料算出一個「章」。驗證:別人用公鑰檢查這個章。對得上,就代表兩件事:這段資料確實出自私鑰主人,而且一個字都沒被改過。

小例子:資料是 app=com.example.shop,附上簽章。攻擊者把它改成 app=com.evil.shop,簽章就對不上了,驗證失敗。

那攻擊者重新打包後,自己再簽一次不就好了?可以簽,但他只能用自己的私鑰,蓋出來的章跟原廠的不一樣。所以簽章擋的不是「有沒有簽」,而是「是不是對的人簽的」。

但還有一個問題:伺服器怎麼知道這把公鑰真的屬於「平台認可的真裝置」,而不是攻擊者自己生的?

答案是憑證(certificate):一張寫著「這把公鑰屬於誰」的文件,由上一層的單位蓋章擔保。上一層又被更上一層擔保,一路到最頂端的根憑證(root certificate)。中間那幾層就叫中間憑證,整串合起來叫憑證鏈。
比喻:行員的章由分行主管蓋章擔保,分行主管的章再由總行蓋章擔保;你只要認得總行的章,就能一路信到行員的章。

根憑證平台公開的伺服器事先就存好 中間憑證由根憑證蓋章擔保 裝置金鑰憑證由中間憑證蓋章擔保 被簽的資料由裝置裡的私鑰簽名 擔保擔保簽名 伺服器驗證:從資料往回,一層一層對到根 每層的章都對、最後接上事先存好的根 → 才相信

圖 2:上面的線=誰替誰擔保;下面的線=伺服器驗證的方向(點圖可放大)

誰來驗這條鏈,看平台:Apple 的做法要你的伺服器自己驗,一路接到 Apple 的根;也有平台是由平台伺服器替你驗,你只拿回結果(Level 2 再講)。

一句話:簽章證明「資料出自這把私鑰、沒被改」;憑證鏈證明「這把私鑰是平台認可的」。

積木二:硬體保護的金鑰——印章鎖在保險箱裡

問題:如果私鑰就放在手機的一般儲存空間,root 過的手機可以直接把它複製走,拿去別的地方簽。

解法:手機裡有一塊跟主系統隔開的安全區。私鑰在裡面產生,永遠不出來;App 只能把資料遞進去,請它簽好再拿回來。

比喻:櫃檯後面上鎖的蓋章機。你可以遞紙進去請它蓋章,但沒辦法把印章拿走。

不同平台的名字不一樣,概念相同:

要記住一個細節:拿不走,不代表不能用。Android 文件說得很清楚:App 被攻破時,攻擊者偷不到金鑰,但仍然可以在那支手機上使用它。這點在「做不到什麼」會再用到。

兩家用法不一樣:

一句話:私鑰在安全硬體裡產生、永遠不離開,所以簽章只可能來自那一支實體裝置。

積木三:challenge——讓每張保證書只能用一次

問題:攻擊者從一支真手機拿到一張合法證明,然後在模擬器上一直重複附上它。簽章是真的、憑證鏈也是真的,前兩塊積木都擋不住。

解法:伺服器先發一個 challenge(也常叫 nonce,意思是「只用一次的數字」):一串隨機、猜不到的值。平台產生證明時,要把它一起簽進去。

伺服器收到證明後,檢查三件事:

  1. 對不對:裡面的 challenge 是不是我剛剛發的那個?
  2. 用過沒:用過一次就作廢,第二次來就拒絕。
  3. 過期沒:只接受在短時間內回來的。

兩家做法不太一樣:

上面「三件事」是自己發 challenge 時的檢查法;平台代管時,同樣的事由平台替你做掉一部分。

一句話:challenge 讓證明綁定「這一次」,錄下來再送也沒用。

一張圖看完整個流程

把三塊積木放回流程裡,就是下面這張圖。先看線怎麼流,再看下面的步驟說明。

平台作業系統+安全硬體+平台伺服器 App使用者手機上 你的伺服器唯一可信的裁判 ① 我要一個 challenge ② 給你一串亂數(我記下來) ③ 請幫我出證明(附 challenge) ④ 簽好名的證明 ⑤ 證明+原本的請求 ⑥ 驗證自己驗,或請平台伺服器代驗 ⑦ 放行/拒絕/另外處理

圖 3:通用流程。有的平台不用你發 challenge、改由平台防重送;但「平台作證 → 伺服器端驗 → 你決定」的骨架都一樣(點圖可放大)

  1. 要 challenge:App 先跟伺服器要一串一次性的亂數。
  2. 記下來:伺服器發出 challenge,同時記住它(之後要比對、要作廢)。
  3. 請平台出證明:App 把 challenge 交給平台。
  4. 平台簽名:安全硬體裡的金鑰替這台裝置作證,平台(含它的伺服器)把 App 身分、裝置狀態、這次的 challenge 一起簽成證明。
  5. 轉交:App 把證明和真正要做的請求一起送給伺服器。
  6. 在伺服器端驗證:有的平台要你自己驗憑證鏈,有的平台由平台伺服器替你驗、你拿回結果;兩種都不在手機上驗。無論哪種,都要確認 challenge 對得上、是自己的 App。
  7. 做決定:通過就放行;不通過要拒絕還是改走別的驗證,是伺服器的政策,Level 3 再談。
一句話:伺服器出一張一次性的單子 → 平台在保險箱裡蓋章 → 伺服器自己驗章。

證明做得到什麼、做不到什麼

先講結論:證明是一個很強的「風險訊號」,但不是百分之百的保證。

✅ 做得到⚠️ 做不到/要小心
讓改過的 App、模擬器、腳本很難拿到有效證明,大幅墊高攻擊成本。不是 100%。Apple 自己承認:改了作業系統的攻擊者可能繞過限制,也無法絕對確定地偵測詐騙。
給伺服器一個攻擊者很難偽造的判斷依據。一支被攻破的真手機,可能替很多假請求出證明(呼應「拿不走,但能用」)。
challenge 讓錄下來重送失效。不能單獨使用。Google 說它最好搭配其他訊號,不該是唯一的防濫用機制;OWASP 也把這類保護定位為額外的保護,App 仍要依威脅模型滿足其他控制項(例如登入驗證)。

誤判:正常使用者也可能被擋

所以「沒拿到證明」不等於「一定是壞人」。失敗時要怎麼處理,Level 3 再談。

隱私:要讓平台參與,就得交出一些資訊

一句話:證明能大幅降低風險,但會誤判、有隱私成本、也不是萬能。它是「加分的訊號」,不是「唯一的門」。

✔ 過關標準

能不看筆記回答下面 5 題,這關就過了。

  1. App 自己偵測「沒被 root」再回報給伺服器,為什麼不夠?看答案收合
    因為檢查程式本身跑在攻擊者手上。重新打包可以刪掉它,hook 可以把結果換成「安全」,腳本甚至不需要 App。
  2. 設備證明是誰、替誰、擔保什麼?誰負責驗?看答案收合
    平台(作業系統+安全硬體+平台伺服器)替 App 擔保「這是正版 App、跑在真的裝置上」。App 只轉交;驗證在伺服器端做(你的伺服器自己驗,或請平台伺服器代驗)。
  3. 簽章、憑證鏈、硬體金鑰各擋掉哪一種造假?看答案收合
    • 簽章:擋「內容被改」。
    • 憑證鏈:擋「攻擊者自己生一把金鑰冒充」。
    • 硬體金鑰:擋「把私鑰複製到別處使用」。
  4. 自己發 challenge 時,伺服器收到證明要檢查哪三件事?看答案收合
    是不是我發的、有沒有用過、有沒有過期。三者都過才算數,這樣錄下來重送就沒用。
  5. 舉兩個「證明做不到」的例子。看答案收合
    任兩個即可:改了作業系統的攻擊者可能繞過;一支被攻破的真手機能替很多假請求出證明;不支援的裝置或自刷系統的正常人會被誤擋;不能當唯一的防線。

下一站

Level 1 會拆開 Apple 的實作,看這套骨架在 iPhone 上長什麼樣。

參考來源