PlainApp 安全性深度剖析
PlainApp 如何在本地 Wi-Fi 上保護您的資料——附開源程式碼佐證。
為什麼本地網路安全很重要?
當您使用應用程式從電腦瀏覽器管理 Android 手機時,您的簡訊、照片、聯絡人和檔案都會透過 Wi-Fi 網路傳輸。大多數應用程式會以可讀的明文傳送這些資料。同一網路上的任何人都能看到。
您可能會想:「這是我家的 Wi-Fi,誰會偷看?」但請考慮:
- 公共 Wi-Fi — 咖啡廳、機場、飯店——附近任何人都能擷取流量
- 共用網路 — 室友、訪客,或被入侵的物聯網裝置
- 路由器漏洞 — 過時的韌體存在已知的安全漏洞
如果應用程式透過 HTTP 以明文傳送您的簡訊,任何人只要使用 Wireshark 等免費工具就能讀取。這不是理論——實際操作極為容易。
大多數手機管理應用程式正是這樣做的。在使用它們的網頁介面時,打開 Chrome DevTools(F12 → Network 分頁),您就能以純 JSON 格式讀取每個 API 呼叫——簡訊內容、檔案路徑、聯絡人姓名,一覽無遺。
PlainApp 有何不同
PlainApp 採用多層加密與驗證機制。以下用淺顯的語言說明每一層的作用。
GraphQL API——只請求您需要的資料
大多數應用程式使用舊式 REST API:一個資源對應一個 URL,不管您是否需要都會回傳所有資料。PlainApp 使用 GraphQL——一種現代化的方式,瀏覽器只請求確切需要的資料,不多不少。
為什麼這很重要:
- 減少資料暴露 — 如果您只需要檔案名稱,那就只傳送檔案名稱
- 單一端點 — 只有一個 /graphql URL 而非數十個,減少了攻擊面
- 逐查詢權限 — 每個查詢都會檢查自己的權限(例如,讀取簡訊需要 READ_SMS 權限,即使您已經登入)
加密 API 呼叫(XChaCha20-Poly1305)
這是 PlainApp 與大多數競爭對手之間最大的差異。
每一個 API 請求和回應——包括 HTTP 和 WebSocket 流量——都使用 XChaCha20-Poly1305 加密,HTTP 和 HTTPS 模式均如此。這是 WireGuard VPN、Cloudflare 和 Google Tink 都在使用的現代 AEAD 加密演算法。
簡單來說:
- 1您的瀏覽器建立一個請求(例如「顯示我的簡訊」)
- 2附加一個時間戳記和隨機 nonce,用於防重放保護
- 3用 XChaCha20-Poly1305 將整個請求體加密成不可讀的二進位資料
- 4加密後的二進位資料直接在網路上傳輸——不轉 base64,不包 JSON,純二進位
- 5只有您的手機能解密和驗證
即使有人攔截了流量,他們看到的只是無意義的二進位資料——而不是可讀的 JSON。
關於加密技術:
- 256 位元金鑰(強度極高)
- 192 位元 nonce(防止某些影響其他加密演算法的攻擊類型)
- 內建竄改偵測——如果任何人在傳輸中修改資料,解密將會失敗
- Android 端:由 Google Tink 驅動
- Web 端:由 @noble/ciphers 驅動(一個經過良好審計的 JavaScript 加密函式庫)
數位簽章(Ed25519)——裝置間通訊
當兩台 PlainApp 裝置互相通訊時(例如手機對手機聊天),僅加密還不夠——每台裝置需要驗證訊息確實來自對方,而非偽造的。
時間戳記
接收端裝置使用發送方的公鑰驗證簽章,並拒絕時間戳記超過 5 分鐘的請求。這同時防止了偽造和重放攻擊。
安全裝置配對(ECDH 金鑰交換)
當兩台 PlainApp 裝置互相配對時(例如兩支手機),它們需要一組共用加密金鑰。但透過網路傳送金鑰就失去了意義。
PlainApp 使用 ECDH(Elliptic Curve Diffie-Hellman)金鑰交換來解決這個問題——這與 TLS、Apple 的 Secure Enclave 和 WebAuthn 使用的是相同的數學原理:
- 1每台裝置產生一組金鑰對(公鑰 + 私鑰)
- 2它們只交換各自的公鑰
- 3每台裝置將自己的私鑰與對方的公鑰結合
- 4雙方得到相同的共享密鑰——而密鑰從未在網路上傳輸
加密 URL——隱藏檔案路徑
這是大多數人不會注意到的細節:URL 路徑也會洩漏資訊。
當您下載檔案時,URL 會告訴伺服器您想要哪個檔案。在許多應用程式中,URL 看起來像這樣:
/fs?path=/sdcard/DCIM/Camera/photo_2024.jpg任何監聽流量的人(或查看瀏覽器歷史紀錄的人)都能準確看到您存取了哪個檔案,以及您手機的資料夾結構。
PlainApp 會加密 URL 中的檔案路徑:
/fs?id=AxK9f2mQ7vR3xP8nWz...id 參數是使用 XChaCha20-Poly1305 加密的檔案路徑,加密金鑰稱為 URL Token。預設情況下,這個金鑰是永久性的,即使重新啟動 App 也不會改變,因此分享過的連結始終有效。沒有金鑰,任何人都無法猜測真實路徑、列舉您的檔案,或偽造下載 URL。
HTTPS 搭配自簽憑證
PlainApp 在您的手機上產生自簽 TLS 憑證。這會建立一條加密的 HTTPS 通道——與銀行和網站使用的技術相同。
憑證使用:
- ECDSA 搭配 P-256 曲線(與主要網站相同)
- SHA256 簽章
- 儲存在應用程式的私有目錄中
結合應用層的 XChaCha20 加密,為您提供兩層獨立的加密保護。破解其中一層,若不同時破解另一層,仍無法取得資料。
登入保護
PlainApp 限制登入嘗試次數,以防止密碼暴力破解:
- 每個 IP 位址每 60 秒最多 5 次嘗試
- 超過後,連線將被拒絕
- 過期的速率限制紀錄會自動清除
HTTP 與 HTTPS:一段誠實的說明
PlainApp 同時提供 HTTP 和 HTTPS 模式。以下是它們的真實差異:
| HTTP 模式 | HTTPS 模式 | |
|---|---|---|
| 檔案路徑 URL | 已加密(XChaCha20) | 已加密(XChaCha20) |
| API 呼叫 | 已加密(XChaCha20) | 已加密(XChaCha20 + TLS) |
| 檔案下載 | 傳輸中未加密 | 由 TLS 加密 |
| 適合誰 | 初學者(無瀏覽器 SSL 警告) | 注重隱私的使用者 |
兩種模式都使用 XChaCha20-Poly1305 加密 API 呼叫——無論使用 HTTP 或 HTTPS,您的簡訊、聯絡人和其他資料始終是加密的。
HTTP 模式存在的原因是自簽憑證會觸發瀏覽器警告(「您的連線不安全」)。對於技術背景較少的使用者,這個警告令人困惑且不安。HTTP 避免了這個問題。
然而,HTTP 模式不會加密檔案傳輸。如果您在共用或公共網路上,請務必使用 HTTPS。
建議:盡可能使用 HTTPS。接受瀏覽器的自簽憑證警告——對於本地網路應用程式來說,這是正常且預期的。
供應鏈安全——更少的相依套件,更低的風險
一個許多使用者不會想到的安全威脅:供應鏈攻擊。當應用程式使用數百個第三方函式庫時,其中任何一個都可能被入侵——注入惡意程式碼來竊取資料、挖掘加密貨幣,或開啟後門。
這在 JavaScript 生態系統中尤其危險。近年來,event-stream、ua-parser-js 和 colors 等熱門 npm 套件被劫持,將惡意程式碼注入數百萬個專案。如果手機管理應用程式的網頁介面引入了數百個 JS 套件,每一個都是潛在的攻擊媒介。
PlainApp 採取刻意的做法:盡可能減少相依套件,在合理的情況下自行撰寫程式碼。
Android 應用程式(plain-app)
- 不使用依賴注入框架(沒有 Hilt,沒有 Dagger)——使用簡單的 singleton
- 加密僅由 3 個知名函式庫處理:Google Tink、Bouncy Castle 和內建的 Java Cryptography Extension
- 伺服器執行於 Ktor(JetBrains 官方的 Kotlin 框架)——不使用冷門的第三方伺服器函式庫
網頁介面(plain-desktop)
僅有 28 個執行時相依套件——遠少於一般網頁應用程式。數項通常需要 npm 套件的功能都是從零開始建構:
| 功能 | 一般應用程式 | PlainApp |
|---|---|---|
| 通知音效 | howler.js / tone.js | 自製 Web Audio API 合成器 |
| UUID 產生 | uuid npm 套件 | 手寫的 RFC 4122 實作 |
| 檔案上傳 | uppy / axios | 自製分塊上傳與佇列 |
| WebRTC 視訊 | Twilio / Daily.co SDK | 手寫的 PeerConnection 程式碼 |
| HTTP 客戶端 | axios / ky | 原生瀏覽器 fetch |
Web 端的加密使用 @noble/ciphers——一個經過良好審計、零相依的 JavaScript 加密函式庫——而非引入龐大臃腫的替代方案。
為什麼這很重要
您加入的每個相依套件都是您無法控制的程式碼。PlainApp 的做法意味著:
- 惡意程式碼更難藏身
- 更小的攻擊面
- 更容易審計(整個程式碼庫是開源的)
- 不會因為被棄用或劫持的 npm 套件而面臨風險
許多競爭對手的常見問題
這裡不針對特定應用程式。而是關於這個類別中的常見模式:
| 安全層面 | PlainApp | 競爭對手常見做法 |
|---|---|---|
| API 流量 | 已加密(XChaCha20),HTTP 和 HTTPS 皆然 | 透過 HTTP 傳送純 JSON |
| WebSocket 流量 | 加密二進位幀(XChaCha20) | 明文 JSON 或不使用 WebSocket |
| 認證 Token 處理 | 用作加密金鑰,從不放在請求標頭中 | 放在 HTTP 請求標頭中(容易洩露) |
| 檔案 URL | 加密的路徑 ID | 暴露明文路徑 |
| 金鑰交換 | ECDH(P-256) | 無 |
| 請求簽章 | Ed25519(僅裝置間通訊) | 無 |
| 防重放保護 | 30 秒 + nonce(Web);5 分鐘 + 簽章(裝置間) | 無 |
| 登入保護 | 頻率限制 + 雙重確認 | 無限制 |
| 資料路由 | 100% 本地,無雲端 | 部分透過雲端路由 |
| 相依套件 | 最少量、知名函式庫 | 數百個 npm 套件 |
| 原始碼 | 開源(GPL-3.0) | 大多為閉源 |
最簡單的檢查方式:在使用應用程式時打開 Chrome DevTools(F12)→ Network 分頁。如果您能以 JSON 格式讀取 API 回應,那麼該應用程式沒有加密。
自動化、可追溯且抗竄改的發佈流程
所有安裝套件都由 GitHub 與 F-Droid 基礎設施自動建置。建置流程可追溯、安全,且不留人工建置介入痕跡。PlainApp 是少數同時提供可驗證建置(SLSA)與 VirusTotal 掃描發佈的 Android 應用之一。
GitHub 與 F-Droid 自動建置
發佈產物由 CI 流程生成,而非本地手動打包。每個產物都可追溯到原始碼提交與可重現建置流程。
SLSA Provenance(第 3 級)+ VirusTotal 掃描
每次發佈都包含 SLSA provenance 證明與 VirusTotal 掃描結果,讓供應鏈可稽核,並幫助使用者在安裝前驗證完整性。
常見問題
使用 HTTP 模式時,PlainApp 真的有加密嗎?
是的。所有 API 呼叫(簡訊、聯絡人、檔案列表等)在 HTTP 和 HTTPS 模式下都使用 XChaCha20-Poly1305 加密。HTTP 模式僅檔案下載未加密。若要完整保護,請使用 HTTPS。
為什麼我的瀏覽器顯示安全警告?
PlainApp 使用自簽 TLS 憑證進行加密。由於它不是由公共憑證授權機構核發的,您的瀏覽器會顯示警告。對於本地網路應用程式來說,這是正常且預期的。點選「進階」然後「繼續」即可。
同一 Wi-Fi 上的人能看到我的資料嗎?
使用 PlainApp 時,不能。API 呼叫使用 XChaCha20-Poly1305 加密,在 HTTPS 模式下檔案傳輸也由 TLS 加密。使用大多數競爭對手的應用程式時,可以——它們的 API 呼叫以純文字 JSON 傳送。
PlainApp 與 VPN 相比如何?
VPN 加密的是您的裝置與 VPN 伺服器之間的流量。PlainApp 加密的是本地網路上手機與瀏覽器之間的流量。兩者用途不同——PlainApp 保護本地裝置管理,而 VPN 保護網際網路流量。
程式碼真的是開源的嗎?
是的。PlainApp 採用 GPL-3.0 授權。完整原始碼可在 github.com/plainhub/plain-app 取得。您可以檢視每個加密實作、從原始碼建構,或做出貢獻。