Skip to content

評估:圖片上傳遷移至 Cloudflare R2 #183

Description

@abc873693

背景

目前 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 的第三方服務

優缺點

優勢

  • 無 egress 費用、可綁自訂 domain、保留政策可控
  • 不再依賴第三方免費方案(Imgur 曾封鎖台灣 IP,見 Move from Imgur other service #139
  • 消除硬編碼 public key 的長期風險

成本 / 劣勢

  • 需新增維運一個 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 是公告編輯頁,照理只有管理員/登入使用者會用:

  1. Worker 只接受帶有 ap_common App 登入 token 的請求
  2. Worker 驗證 token → 簽發短期 presigned R2 URL(例如 5 分鐘有效)
  3. 搭配 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)

  • 抽出 ImageUploader abstract interface,讓下游 App 可注入自訂實作
  • 移除 edit_page.dart 中對 ImgbbHelper 的硬依賴
  • 修正按鈕文案 pickAndUploadToImgur(與實際行為不符)

待決議

  1. 是否值得為了圖片上傳新增 Worker 維運負擔?
  2. 若採用 R2,多 App 共用 bucket vs. 各自 bucket?
  3. 認證策略:是否能重用下游 App 的既有登入 token?各 App 的 auth 機制是否一致?
  4. 短期替代方案:是否先完成 ImageUploader 抽象,把選擇權交給下游?

Related: #139

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions