背景
目前 ap_common_announcement_ui 的圖片上傳由 ImgbbHelper / ImgurHelper 實作(位於 packages/ap_common_announcement_ui/lib/src/api/),兩者皆以硬編碼的匿名 client key 直接從 Flutter client 上傳。
延續 #139 的方向,評估進一步遷移至 Cloudflare R2 以降低對第三方免費圖床的依賴。
現況
ImgbbHelper.uploadImage(XFile) 為 edit_page.dart 唯一使用的上傳器(按鈕文案仍為 pickAndUploadToImgur,實際呼叫 Imgbb)
- 兩個 helper 沒有共同 abstract interface,為 duck-typed(皆回傳
Future<ApiResult<String>>)
- API key 硬編碼在 class 中,無 runtime 注入機制
- 無檔案大小 / 格式預檢,僅靠 HTTP 400 回報
notSupportImageType
R2 的核心限制
R2 使用 S3 相容簽章認證,不能把 Access Key 放進 client app,必須搭配後端或 presigned URL。與 Imgur/Imgbb 的 anonymous upload 模式根本不同。
候選方案
| 方案 |
說明 |
成本 |
複雜度 |
| A |
Cloudflare Worker 產生 presigned PUT URL,client 直接 PUT |
Worker 免費額度內 + R2 儲存費 |
中 |
| B |
Worker 作為 upload proxy,client 傳給 Worker,Worker 寫 R2 |
Worker CPU time 較貴 |
低 |
| C |
放棄 R2,沿用有 anonymous upload 的第三方服務 |
— |
— |
優缺點
優勢
成本 / 劣勢
- 需新增維運一個 Cloudflare Worker(目前 ap_common 生態無自有 backend)
- 多下游 App(NKUST / NSYSU / WTUC…)需設計共用或各自 bucket(多租戶)
- Abuse 防護需自行處理(rate limit、referer、檔案大小/類型驗證)
認證策略(若採用 Worker + R2)
前提:Mobile App 是可反編譯的 binary,無法 100% 證明「請求來自官方 App」。目標是疊加多層防線把 abuse 成本拉高。
候選機制
| 機制 |
強度 |
成本 |
備註 |
| Shared header / API key |
★ |
幾乎零 |
只擋懶鬼 bot,與現況同風險 |
| Cloudflare Turnstile |
★★ |
免費、有 mobile SDK |
隱形 CAPTCHA |
| Play Integrity API (Android) |
★★★★ |
Google 免費額度內 |
驗證 Play Store 版本、未 root |
| App Attest (iOS) |
★★★★ |
Apple 免費 |
驗證 bundle ID + 裝置 |
| 綁定既有登入(推薦) |
★★★★★ |
幾乎零(重用現有 auth) |
見下方 |
推薦流程:登入綁定 + 短期 presigned URL
edit_page.dart 是公告編輯頁,照理只有管理員/登入使用者會用:
- Worker 只接受帶有 ap_common App 登入 token 的請求
- Worker 驗證 token → 簽發短期 presigned R2 URL(例如 5 分鐘有效)
- 搭配 IP + 帳號雙層 rate limit(純 IP 會因 CGNAT / 校園網路誤殺)
Platform Attestation(Play Integrity / App Attest)作為 optional 加強層。
Rate limit 實作備選
- Workers Rate Limiting API(binding,GA、免費方案可用)— 最簡單
- Durable Objects — 需滑動視窗或跨區強一致時使用
- WAF Rate Limiting Rules — Dashboard 設定,進階規則需 Pro+
取 client IP 一律用 CF-Connecting-IP(邊緣注入、無法偽造),不要用 X-Forwarded-For。
建議的前置工作(不論是否採用 R2)
待決議
- 是否值得為了圖片上傳新增 Worker 維運負擔?
- 若採用 R2,多 App 共用 bucket vs. 各自 bucket?
- 認證策略:是否能重用下游 App 的既有登入 token?各 App 的 auth 機制是否一致?
- 短期替代方案:是否先完成
ImageUploader 抽象,把選擇權交給下游?
Related: #139
背景
目前
ap_common_announcement_ui的圖片上傳由ImgbbHelper/ImgurHelper實作(位於packages/ap_common_announcement_ui/lib/src/api/),兩者皆以硬編碼的匿名 client key 直接從 Flutter client 上傳。延續 #139 的方向,評估進一步遷移至 Cloudflare R2 以降低對第三方免費圖床的依賴。
現況
ImgbbHelper.uploadImage(XFile)為edit_page.dart唯一使用的上傳器(按鈕文案仍為pickAndUploadToImgur,實際呼叫 Imgbb)Future<ApiResult<String>>)notSupportImageTypeR2 的核心限制
R2 使用 S3 相容簽章認證,不能把 Access Key 放進 client app,必須搭配後端或 presigned URL。與 Imgur/Imgbb 的 anonymous upload 模式根本不同。
候選方案
優缺點
優勢
成本 / 劣勢
認證策略(若採用 Worker + R2)
前提:Mobile App 是可反編譯的 binary,無法 100% 證明「請求來自官方 App」。目標是疊加多層防線把 abuse 成本拉高。
候選機制
推薦流程:登入綁定 + 短期 presigned URL
edit_page.dart是公告編輯頁,照理只有管理員/登入使用者會用:Platform Attestation(Play Integrity / App Attest)作為 optional 加強層。
Rate limit 實作備選
取 client IP 一律用
CF-Connecting-IP(邊緣注入、無法偽造),不要用X-Forwarded-For。建議的前置工作(不論是否採用 R2)
ImageUploaderabstract interface,讓下游 App 可注入自訂實作edit_page.dart中對ImgbbHelper的硬依賴pickAndUploadToImgur(與實際行為不符)待決議
ImageUploader抽象,把選擇權交給下游?Related: #139