版数: 2.1 | 更新日: 2026-06-15 | 層: ② 設計
設計資料
この文書について(標準設計書の構成)
目的 システムがどう動くか を、実装・レビュー・運用の共通理解として固定する
想定読者 開発者、インフラ担当、レビュア、山梨大連携担当
いつ読むか 機能追加前、セキュリティレビュー、障害の影響調査時
§1 基本設計
1. 目的
競馬向け馬体診断アプリケーションにおいて、フィードバック収集・AI 再学習・
アプリ再ビルド・端末配布までを含む継続運用可能なインフラ を構築・検証する。
アプリケーションの診断精度そのものは本プロジェクトの検証対象外とする。
2. システム構成(論理)
領域 コンポーネント 役割
クライアント
モバイルアプリ(スタブ)
撮影(または画像選択)、端末内推論、FB 送信
エッジ
Cloudflare Edge + cloudflared
内向き FB 通信を Edge で受け、Tunnel 経由で自宅 PC の FastAPI へ中継(CGNAT 回避)。PoC の poc-api-tunnel(api.dammy-otoko.com)を再利用
サーバー(自宅 PC)
FastAPI / DB / Storage / AI Worker
FB 保存、再学習、新モデル生成、Webhook 送信。ホストは Windows 11 PC(2026-06-07 〜)。開発経緯は 開発ログ
CI/CD
GitHub Actions / Fastlane(②のみ)
モデル同梱アプリの自動ビルド・配布
配布
APK 直配布(①)/ TestFlight(②)
管理下端末へ更新を届ける
3. 検証スコープ
区分 対象 対象外
インフラ
FB 受信、再学習トリガー、GHA ビルド、配布、Webhook 連携
—
アプリ
スタブによる通信・ビルド同梱の確認
診断精度、UI/UX の本番品質
AI
モデルファイルの更新・同梱フロー
学習アルゴリズムの精度検証
4. 段階的検証方針
Phase ① Android: プラットフォーム非依存のパイプラインを確立(Ubuntu ランナー、TFLite、APK 配布)
Phase ② iPhone: ①の手順・構成を踏襲し、macOS ランナー・CoreML・TestFlight を追加
5. 非機能要件
項目 要件
運用性 手順書(HTML runbook)に従い、第三者が再現可能であること
コスト ①では Apple Developer Program 不要。GHA 無料枠内での運用を目指す
セキュリティ FB データに個人情報・位置情報を含めない。Tunnel 経由の HTTPS 通信。前提知識は K-24 〜K-28 、設計は セキュリティ設計
更新頻度 月次 1 回のモデル更新・配布を想定
6. 技術スタック(検証時)
レイヤー ① Android 検証 ② iPhone 検証
クライアント Flutter(スタブ) Flutter または Swift(既存資産に合わせる)
オンデバイス ML TensorFlow Lite CoreML
API FastAPI FastAPI(共通)
DB PostgreSQL または SQLite 同上
CI/CD GitHub Actions(ubuntu-latest) GitHub Actions(macos-latest)
配布 APK artifact + adb TestFlight
§2 詳細設計
本資料は検証実装に伴い更新する。未確定項目は「TBD」と記載する。
1. コンポーネント一覧
ID コンポーネント 配置 実装(予定)
C-01 スタブモバイルアプリ クライアント Flutter
C-02 Cloudflare Tunnel 自宅 PC(Docker) cloudflared(PoC の poc-api-tunnel を再利用。公開 URL: https://api.dammy-otoko.com)
C-03 Web API 自宅 PC(Docker) FastAPI + Uvicorn
C-04 データベース 自宅 PC(Docker) PostgreSQL または SQLite
C-05 ファイルストレージ 自宅 PC ローカルディレクトリ
C-06 AI Worker 自宅 PC(Docker) Python(スタブ)
C-07 CI/CD GitHub GitHub Actions
2. FB 受信 API(C-03)
項目 内容
エンドポイント POST /api/v1/feedback、GET /api/v1/health
Content-Type multipart/form-data
リクエスト 画像ファイル、推論結果(JSON)、タイムスタンプ
レスポンス 201 Created + フィードバック ID
処理 画像を Storage へ保存、メタデータを DB へ記録
3. AI Worker(C-06)スタブ仕様
項目 内容
トリガー 手動実行、または cron / Webhook(検証時は手動可)
入力 DB / Storage 内の FB データ(スタブでは参照のみでも可)
出力 新バージョンのモデルファイル(TFLite / CoreML)
完了時処理 GitHub repository_dispatch を送信
4. GitHub Actions ワークフロー(C-07)
4.1 android-build.yml(①)
項目 内容
トリガー workflow_dispatch、repository_dispatch(type: model-updated)
ランナー ubuntu-latest
主な処理 Flutter セットアップ → モデル同梱ビルド → APK artifact 出力
成果物 app-release.apk
4.2 ios-build.yml(②・未実装)
項目 内容
トリガー repository_dispatch(type: model-updated)
ランナー macos-latest
主な処理 署名 → CoreML 同梱ビルド → Fastlane で TestFlight upload
前提 Apple Developer Program、Secrets(証明書・API キー)
5. Webhook 連携
項目 内容
送信元 AI Worker(C-06)
送信先 GitHub REST API: POST /repos/{owner}/{repo}/dispatches
event_type model-updated
client_payload model_version, model_path 等(TBD)
認証 GitHub Personal Access Token または GitHub App(Secrets 管理)
6. ディレクトリ構成(予定)
github-actions-test/
├── .github/workflows/ # GHA ワークフロー
├── docs/ # 本資料(HTML)
├── app/ # Flutter スタブアプリ(TBD)
├── server/ # FastAPI + Worker スタブ(TBD)
└── models/ # モデルファイル配置(TBD)
§3 セキュリティ設計
本番導入に向けたセキュリティ方針・脅威・対策 の設計ドラフトです。
基本設計 の非機能要件「HTTPS・個人情報を含めない」は方針レベルのみであり、
本資料で資産・攻撃面・対策の具体化 を行います。
実装状況(2026-06-14): SEC-01〜03 は
server/api に実装済み。
SEC-04 は起動時警告 +
server/env.example。
FEEDBACK_API_KEY 未設定時は移行用に無認証可(ログに WARNING)。
運用検証(2026-06-14): server/.env にキー設定後、Tunnel 経由
test-feedback.ps1 で Bearer 付き POST が HTTP 201(
K-30 )。
スコープ: 役場サーバー、Cloudflare Tunnel、FB 受信 API、Storage、PostgreSQL、
GitHub Actions / PAT、山梨大 Worker 連携。
アプリ内の推論ロジック・学習データの機密性評価は山梨大側の責務とし、インフラ境界を中心に扱います。
1. 設計原則
原則 内容
最小公開 インターネットから到達できるのは Tunnel 経由の API のみ。DB・管理ポートは非公開
秘密の分離 トークン・DB パスワードは .env / GitHub Secrets。リポジトリに含めない
多層防御 HTTPS(Cloudflare)+ API 認証(本番で追加)+ ホスト保護 + 運用監視
実用優先 小規模運用に過剰な仕組みは避け、ゴーライブ前の必須対策と後追いを分ける
2. 資産一覧
ID 資産 機密性 保管場所
AST-01 FB 画像(馬体写真) 中 data/storage/
AST-02 推論結果 JSON 低〜中 PostgreSQL feedbacks
AST-03 DB 全データ 中 Docker volume pgdata
AST-04 TUNNEL_TOKEN高 server/.env
AST-05 GitHub PAT(Worker dispatch) 高 worker/.env
AST-06 PostgreSQL 認証情報 高 server/.env
AST-07 APK / モデル世代 中 GHA artifact・data/apk-archive/
AST-08 Apple 署名鍵(将来) 高 GitHub Secrets / Match
3. 現状の攻撃面とギャップ
層 現状(検証環境) リスク 本番での対策(案) 優先
通信(Transit)
Cloudflare Tunnel 経由 HTTPS
低(平文露出なし)
本番ドメイン移管後も Tunnel 維持。証明書は Cloudflare 管理
—
API 認証
SEC-01 実装済み (FEEDBACK_API_KEY + Bearer)。未設定時は移行用に無認証可
高(URL 漏洩で不正投稿・容量枯渇)→ 低減
クライアント APK 同梱・ローテーション(実装済み 2026-06-14)
必須
アップロード制限
画像 MIME のみ検証。サイズ上限なし
中(巨大ファイル・DoS)
最大サイズ(例: 10MB)・レート制限(SEC-02)
必須
ネットワーク境界
API 8000 をホストに bind
中(LAN からの直接アクセス)
localhost bind のみ、または FW で 8000 遮断(SEC-03)
高
DB 露出
Docker 内部のみ(ポート未公開)
低
本番も外部非公開を維持。デフォルトパスワード変更(SEC-04)
必須
保存時暗号化
平文ファイル・DB
低〜中(物理盗難・ディスク流出)
Windows BitLocker + バックアップ先のアクセス制御(SEC-05)
中
シークレット管理
.env 手動・gitignore
中(誤コミット・平文保管)
本番パスワード生成・一覧表・ローテーション手順(3.1.2)
必須
Cloudflare Access
未使用
—
管理用エンドポイント追加時に Zero Trust ポリシー検討(TBD)
低
CI/CD
PAT を Worker ローカル保持
中(漏洩でリポジトリ操作可)
最小スコープ PAT・期限・四半期ローテーション(K-23)
高
ログ・個人情報
方針のみ(PII 禁止)
中(アプリ実装依存)
FB スキーマに位置情報・氏名フィールドを持たない設計を維持
中
4. 脅威モデル(簡易)
flowchart LR
subgraph internet [インターネット]
Attacker[不正利用者]
App[本番アプリ]
end
subgraph cf [Cloudflare]
TLS[HTTPS終端]
Tunnel[Named Tunnel]
end
subgraph host [役場PC]
API[FastAPI :8000]
DB[(PostgreSQL)]
Store[Storage]
PAT[worker/.env PAT]
end
subgraph gh [GitHub]
GHA[Actions]
end
App -->|HTTPS FB POST| TLS --> Tunnel --> API
Attacker -.->|URL知得時 無認証POST可| TLS
API --> DB
API --> Store
PAT -->|dispatch| GHA
最大のギャップ: Tunnel は「通信路の保護」であり、「利用者の認証」ではない。
現状、api.dammy-otoko.com/api/v1/feedback の URL を知れば curl から投稿可能です(検証で意図的に開放)。
5. 対策一覧(SEC-xx)
ID 対策 内容 実装タイミング 成果物
SEC-01
API 認証
Authorization: Bearer <API_KEY> または X-API-Key ヘッダ。キーはアプリに埋め込み、サーバー .env で検証
MS-6 前(必須)
API ミドルウェア・FEEDBACK_API_KEY
SEC-02
アップロード制限
最大ファイルサイズ、Content-Type 検証強化。必要なら IP / レート制限(Cloudflare Rate Limiting)
MS-6 前(必須)
FastAPI 設定・Cloudflare ルール(任意)
SEC-03
ポート露出最小化
API を 127.0.0.1:8000 のみに bind。Tunnel は localhost へ接続
MS-6 前(推奨)
docker-compose 変更
SEC-04
DB 強化認証
本番用ランダムパスワード。デフォルト change_me 禁止
3.1.2 と同時
server/.env 本番値
SEC-05
ホスト保護
Windows Update、BitLocker(可能なら)、物理アクセス制限、自動ログイン回避
MS-6 前
役場チェックリスト
SEC-06
シークレットローテーション
PAT・API キー・Tunnel 再発行手順。漏洩時の初動
3.1.2 / 3.6
運用 runbook 追記
SEC-07
依存関係
Docker イメージ・Python パッケージの定期更新方針
MS-7 後(定例)
四半期レビュー
SEC-08
iOS 署名
証明書・Provisioning を Secrets / Match で管理。平文リポジトリ禁止
WBS 2.3
ios-build 手順
6. 層別設計
6-1. 公開 URL の意味(よくある誤解)
https://api.dammy-otoko.com/api/v1/feedback はサーバー PC が「設定した URL」ではありません 。
DNS で Cloudflare を向き、Cloudflare が Tunnel 経由で PC 内の API に転送するインターネット上の入口 です。
端末は Cloudflare のエッジに HTTPS で届ける(直接 PC の IP には行かない)— ご認識どおり
しかし curl も同じ URL を叩ける — Cloudflare は「正規アプリか」を見ない
危険なのは PC IP の漏洩より、この意図して公開した入口に門番(API 認証)がない こと
6-2. 通信・ネットワーク
クライアント → Cloudflare: TLS 1.2+(Cloudflare 既定)
cloudflared → API: localhost(SEC-03 適用後は LAN 非公開)
PostgreSQL: Docker 内部ネットワークのみ。5432 をホストに公開しない
6-3. API・認証(本番案)
# クライアント(アプリ)
POST /api/v1/feedback
Authorization: Bearer <FEEDBACK_API_KEY>
Content-Type: multipart/form-data
# サーバー(.env)
FEEDBACK_API_KEY=<ランダム 32 文字以上>
キー配布: 本番アプリビルド時に環境変数または難読化して同梱。
ローテーション時はアプリ再配布が必要(月次更新と整合)。
HTTPS 上で送れば途中傍受は困難 (TLS の役割)。アプリに埋め込んだキーは逆コンパイルで取られる可能性あり(小規模運用では許容しつつ認識)。
将来、端末ごとのキーや Cloudflare Access は規模拡大時に検討(TBD-06)。
6-4. データ保護
データ 転送中 保存時 保持期間(案)
FB 画像 HTTPS 平文(BitLocker でホスト保護) 学習利用後も蓄積。容量ポリシーは TBD-04
DB レコード — PostgreSQL 平文 バックアップと同期(3.7)
ログ — ローカル / Docker 画像バイナリをログに出さない
6-5. シークレット管理(.env と DB の使い分け)
秘密 保管 ローテーション 漏洩時
TUNNEL_TOKENserver/.envCloudflare で再発行 → compose 再起動 即時再発行。旧トークン失効
FEEDBACK_API_KEYserver/.env + アプリ計画メンテ時に再生成・再配布 即時変更 + アプリ緊急配布
GitHub PAT worker/.env四半期(K-23) GitHub で revoke → 新 PAT
POSTGRES_PASSWORDserver/.env年次 or 漏洩時 DB パスワード変更 + 再起動
POSTGRES_PASSWORDserver/.env年次 or 漏洩時 DB パスワード変更 + 再起動
DB ユーザー/パスワードもシークレット だが、すべての秘密を DB テーブルに入れるのは一般的ではない 。
API 起動時に DB へ接続するため「鶏と卵」になり、運用も複雑化する。
小規模構成では .env(gitignore)+ ファイルアクセス制限が定石。大規模では Vault / クラウド Secret Manager。
7. OSI 参照モデルと FW(本プロジェクト)
FW(ファイアウォール)は主に層 3〜4(IP・TCP/UDP ポート) で「この入口への通信を許可/拒否」する門番です。
flowchart TB
subgraph L7["層7 アプリ: HTTP POST /api/v1/feedback"]
App[本番アプリ]
Curl[curl いたずら]
API[FastAPI]
end
subgraph L6["層6 表現: TLS"]
TLS[HTTPS 暗号化]
end
subgraph L4["層4 輸送: TCP ポート"]
CF443["Cloudflare :443"]
LAN8000["PC LAN :8000 ← 現状 docker expose"]
end
subgraph L3["層3 ネットワーク: IP"]
Internet[インターネット]
LAN[同一 LAN]
end
App --> TLS --> CF443
Curl --> TLS --> CF443
CF443 --> Tunnel[cloudflared Tunnel]
Tunnel --> API
LAN -->|"FW未設定時 到達可"| LAN8000 --> API
層 本プロジェクトの例 現状 あるとよい対策
L7 アプリ FB API・認証ヘッダ 無認証で POST 可 API キー(SEC-01)・サイズ上限(SEC-02)
L6 表現 TLS / HTTPS Cloudflare で有効 維持
L4 輸送 TCP 8000, 443 8000 が PC に公開 8000 を localhost のみ(SEC-03)+ FW で LAN 拒否
L3 ネットワーク IP・ルーティング グローバル IP 非公開(Tunnel) 維持。LAN IP 直接アクセスは FW で制限
DB PostgreSQL 5432 Docker 内部のみ 維持 + 強パスワード
FW をしないと何が危いか(本件): 同一 LAN や誤設定時に http://<PCのLAN IP>:8000 で
Cloudflare・HTTPS・API 認証をバイパス して API に届く可能性(層 7 の門番がないとそのまま通る)。
Raspberry Pi で行った FW 設定は、まさに層 3〜4 で「不要なポートへの入り口を閉じる」作業です。
多層防御: DB 非公開(L4)だけでは不十分。API 侵害=DB も読めるため、L7 の API 認証も必要。
8. Cloudflare Access と Pages のメール認証
概念は同じ (コンテンツの前に「誰か」を確認する)が、製品の適用先が違います 。
Pages のメール認証 Cloudflare Access(本 API 向け)
保護対象 静的 HTML(docs) API / 管理画面など動的オリジン
利用者 人間(ブラウザ) 人間向け。モバイルアプリの自動 POST には不向き
本 FB API — 第一候補ではない(API キーが適する)
9. セキュリティ実測確認(WBS 3.1.5・学習目的)
理論を体験で補強する。実施記録を本節または開発ログに残す。
手順 確認すること コマンド例
1. 正規 FB 送信
アプリ or test-feedback.ps1 で 1 件成功
.\scripts\test-feedback.ps1 -BaseUrl https://api.dammy-otoko.com
2. DB 確認
feedbacks に何が保存されるか(PII の有無)
docker compose exec db psql -U app -d feedback_db -c "SELECT id, left(inference_result,80), created_at FROM feedbacks ORDER BY created_at DESC LIMIT 3;"
3. Storage 確認
画像ファイルの実体・ファイル名
data/storage/ をエクスプローラで確認
4. API ログ
リクエスト body・画像がログに出ないか
docker compose logs api --tail 50
5. 無認証 curl(SEC-01 有効時)
認証なし POST が 401 で拒否されること
curl で /api/v1/feedback(ヘッダなし)→ missing or invalid authorization header
6. 誤った API キー(SEC-01)
正しくない Bearer が 401 で拒否されること
test-feedback.ps1 -ApiKey "wrong-key-on-purpose" → invalid api key
7. LAN 経路(任意)
:8000 直叩きが可能か(FW / bind 設定の理解)
同一 LAN から http://<PC LAN IP>:8000/api/v1/health
実施記録: K-28 実測記録(2026-06-13) 。
SEC-01 本番キー運用・deny 確認: K-30 (deny 実測 、2026-06-14)。
10. ゴーライブ前チェックリスト(MS-6)
# 項目 SEC
1 API 認証が有効で、無認証 POST が 401 になる SEC-01
2 アップロードサイズ上限が動作する SEC-02
3 DB パスワードがデフォルトから変更済み SEC-04
4 .env が git に含まれていないSEC-06
5 PAT スコープが最小(repo dispatch に必要な範囲) SEC-06
6 役場 PC の物理・OS 更新方針が合意されている SEC-05
7 漏洩時エスカレーション先が 運用計画 に記載されている SEC-06
11. 未決定事項(TBD)
ID 項目 選択肢 決定時期
TBD-06 API 認証方式 共有 API キー(推奨・簡易) / Cloudflare Access / mTLS 3.1.4
TBD-07 レート制限 アプリのみ / Cloudflare Rate Limiting / FastAPI ミドルウェア 3.1.4
TBD-08 山梨大 Worker からのサーバーアクセス SSH / Tailscale / データは FB のみ(現状) 3.2
12. 関連資料
§4 データフロー
内向き通信(クライアント → 自宅 PC): CGNAT 環境のため、
スタブアプリから FastAPI への FB 送信は必ず
Cloudflare Edge → Tunnel(cloudflared)経由 とする。
自宅 PC へ直接到達する経路は存在しない。
SEC-01(2026-06-14〜): 経路①に加え、アプリは
Authorization: Bearer を付与する — 詳細は
シーケンス図 §2 、旧来の無認証フローは
§1 。
1. 全体データフロー
flowchart TB
subgraph Client["クライアント(スタブアプリ)"]
FB["FB データ(画像 + 推論結果 JSON)"]
end
subgraph CFNet["インターネット / Cloudflare"]
CF["Edge Server(HTTPS 終端)"]
end
subgraph Server["自宅 PC(Docker)"]
TUN["cloudflared(Tunnel クライアント)"]
API[FastAPI]
DB[(Database)]
STG[(File Storage)]
WK[AI Worker]
end
subgraph CICD["GitHub(インターネット)"]
GHA[GitHub Actions]
ART[APK / IPA artifact]
end
subgraph Deploy["配布"]
DEV[管理下端末]
end
FB -->|"①内向き HTTPS"| CF
CF -->|"Tunnel 中継"| TUN
TUN --> API
API --> STG
API --> DB
DB --> WK
STG --> WK
WK -->|新モデル| STG
WK -->|"②外向き Webhook"| GHA
GHA --> ART
ART -->|"③配布(Tunnel 外)"| DEV
通信方向の整理
番号 方向 経路 備考
①
内向き
スタブアプリ → Cloudflare Edge → cloudflared → FastAPI
FB 受信。CGNAT 回避のため Tunnel 必須。SEC-01 後 は Bearer 必須(抜け道の整理 )
②
外向き
AI Worker → GitHub API(repository_dispatch)
再学習完了後のビルド起動。Tunnel は不使用
③
配布
GHA artifact → 管理下端末(adb / TestFlight)
アプリ更新。FB 経路とは別
2. FB 受信フロー(02-1 相当)
段階 データ 保存先 SEC-01 前 / 後
1. アプリ送信 画像 + 推論結果 + タイムスタンプ — 後: + Authorization
2. Cloudflare Edge HTTPS 終端・CDN — 変わらず(盗聴防止)
3. Tunnel 中継 Edge → cloudflared(自宅 PC)へ転送 — 変わらず
4. API 受信 multipart データ — 後: 鍵検証(不一致 → 401)
5. 永続化 画像ファイル File Storage 201 のときのみ
6. 永続化 メタデータ(ID, 結果, パス, 日時) Database 201 のときのみ
Phase1 初期のギャップ: 段階 2〜3 までは安全でも、段階 4 で「誰の POST か」を見ていなかった(
シーケンス §1 )。
3. モデル更新・配布フロー(02-2 / 02-3 相当)
Phase1 の月次運用イメージ(手動 / 自動の境界、Android adb vs iPhone TestFlight)は
Phase1 ナレッジ K-21 を参照。
flowchart LR
A[FB データ蓄積] --> B[AI Worker 再学習]
B --> C[新モデルファイル]
C --> D[Git 反映 or 配置]
D --> E[repository_dispatch]
E --> F[GHA ビルド]
F --> G[モデル同梱 APK/IPA]
G --> H[端末へ配布]
4. データ要素定義
データ 形式 例 個人情報
FB 画像 JPEG / PNG horse_20260605_001.jpg 含まない
推論結果 JSON {"label":"A","score":0.92}含まない
モデル(①) .tflite model_v202606.tflite —
モデル(②) .mlmodel / .mlpackage model_v202606.mlpackage —
§5 シーケンス図
本資料は主要処理の時系列 を示します。
WBS 1(Phase1 インフラ検証)では §1(旧) の図だけで MS-1〜MS-3 まで進めましたが、
WBS 3.1.4(SEC-01)以降は §2(現行) が正本です。
「HTTPS があるのに何が守れていなかったか」は §3 を参照。
0. 時系列 — どの図がいつ有効だったか
0b. HTTPS / Tunnel が守るもの・守らないもの
Phase1 初期に「Tunnel があれば安全」と誤解しやすい点の整理(K-25 )。
レイヤー Phase1 初期から有効 SEC-01 前のギャップ SEC-01 後
通信路(盗聴)
HTTPS(Cloudflare Edge で TLS 終端)
—
変わらず
到達性(CGNAT 回避)
Named Tunnel(外向き接続のみ)
—
変わらず
送信者の正当性
なし
URL を知れば誰でも POST 可 (curl・スクリプト・偽アプリ)
Authorization: Bearer で API キー検証
health エンドポイント
認証なし
意図的に公開(死活監視)
意図的に公開(POST のみ保護)
1. FB 受信 — 旧(SEC-01 前・2026-06-13 まで)
WBS 1.2.3 / 1.5.2 / MS-1〜3 の検証時点の図。認証ステップが存在しない 。
当時の実装どおり、API は multipart を受け取れば保存した。
sequenceDiagram
autonumber
actor User as 利用者
participant App as スタブアプリ
participant Attacker as 不正送信者(任意)
participant CF as Cloudflare Edge
participant TUN as cloudflared
participant API as FastAPI
participant STG as Storage
participant DB as Database
Note over App,API: SEC-01 前 — Authorization ヘッダなし
User->>App: 画像選択・推論
App->>CF: POST /api/v1/feedback (HTTPS, 画像+JSON)
CF->>TUN: Tunnel 中継
TUN->>API: 転送
API->>STG: 画像保存
API->>DB: メタデータ記録
API-->>App: 201 Created
Note over Attacker,API: 抜け道 — URL さえ分かれば同じ POST が通る
Attacker->>CF: POST(鍵なし curl 等)
CF->>TUN: 中継
TUN->>API: 転送
API-->>Attacker: 201 Created
当時「セキュリティが担保されていた」と言えたこと: 路上の盗聴は TLS で防げる、自宅 PC にポート開放不要。
当時「担保されていなかった」こと: 「正規アプリからの POST か」の区別。公開 URL は実質書き込み用の入口 だった(K-26 )。
2. FB 受信 — 現行(SEC-01 有効・2026-06-14 〜)
server/.env に FEEDBACK_API_KEY があるとき。
アプリはビルド時 --dart-define で同じ鍵を同梱(runbook §4b )。
sequenceDiagram
autonumber
actor User as 利用者
participant App as スタブアプリ
participant Attacker as 不正送信者
participant CF as Cloudflare Edge
participant TUN as cloudflared
participant API as FastAPI
participant Auth as SEC-01 認証
participant STG as Storage
participant DB as Database
Note over App: APK 内 feedbackApiKey (ビルド時 dart-define)
User->>App: 画像選択・推論
App->>CF: POST + Authorization Bearer 鍵
CF->>TUN: Tunnel 中継(TLS 済み)
TUN->>API: 転送
API->>Auth: 鍵一致?
Auth-->>API: OK
API->>STG: 画像保存
API->>DB: メタデータ
API-->>App: 201 Created
Note over Attacker: 鍵なし / 誤キー
Attacker->>CF: POST(Authorization なし)
CF->>TUN: 中継
TUN->>API: 転送
API->>Auth: 鍵一致?
Auth-->>API: NG
API-->>Attacker: 401 Unauthorized
health (GET /api/v1/health)は従来どおり認証不要。
保護対象は POST /api/v1/feedback のみ(K-30 deny 実測済み)。
3. 抜け道の整理 — Phase1 で実測したもの
抜け道 SEC-01 前 SEC-01 後 記録
公開 URL への無認証 POST
可能 (201)
不可 (401)
K-28 手順5 → K-30 deny
誤った API キー
—(検証なし)
401 invalid api key
K-30 deny
localhost / LAN から :8000 直接
health 到達可(Tunnel バイパス)
SEC-03 で bind 方針を検討。POST も鍵が必要
K-28 手順6 、SEC-03
古い APK(鍵なし)からの POST
—
401(鍵更新後に旧 APK は使えない)
鍵ローテーション時に再ビルド・再配布
APK 逆解析で鍵抽出
—
理論上可能(モバイル API キーの限界)
セキュリティ設計 — 無認証よりマシ、完全防御ではない
3b. 不正 POST のイメージ(SEC 前)
flowchart LR
subgraph ok [守れていたこと]
TLS["HTTPS: 盗聴防止"]
TUN["Tunnel: CGNAT 回避"]
end
subgraph ng [守れていなかったこと]
URL["公開 URL の共有"]
CURL["curl / 任意クライアント"]
URL --> CURL
CURL --> API["FastAPI POST → 201"]
end
4. 再学習〜配布(02-2 / 02-3)— 認証の別系統
FB 受信(SEC-01)とは別の秘密 が絡む経路。混同注意(K-31 )。
sequenceDiagram
autonumber
actor Op as 運用者
participant WK as AI Worker(PC)
participant STG as Storage
participant DB as Database
participant GH as GitHub API
participant GHA as GitHub Actions
participant Dev as 管理下端末
Op->>WK: run-training-stub.ps1
WK->>DB: FB データ参照(ローカル)
WK->>STG: モデル生成
Note over WK,GH: PAT(worker/.env)で認証 FEEDBACK_API_KEY とは無関係
WK->>GH: repository_dispatch + PAT
GH->>GHA: android-build.yml 起動
Note over GHA: secrets.FEEDBACK_API_KEY → dart-define → APK
GHA->>GHA: Flutter ビルド
GHA-->>Dev: artifact → adb install
5. 手動ビルド検証(workflow_dispatch)
sequenceDiagram
actor Op as 運用者
participant GHA as GitHub Actions
participant Sec as Actions Secret
participant Art as Artifact
Op->>GHA: Run workflow(GitHub UI)
GHA->>Sec: FEEDBACK_API_KEY 読み込み
Sec-->>GHA: ビルド時 dart-define へ
GHA->>GHA: flutter build apk --release
GHA->>Art: APK 出力
Op->>Art: ダウンロード → adb install
UI からの手動起動に PAT は不要。APK への鍵同梱には Actions Secret が必要(K-22 )。
6. ② iPhone 配布(参考・未検証)
sequenceDiagram
participant GHA as GitHub Actions
participant FL as Fastlane
participant ASC as App Store Connect
participant TF as TestFlight
participant iPhone as iPhone
GHA->>FL: iOS ビルド・署名
FL->>ASC: アップロード
ASC->>TF: ビルド公開
iPhone->>TF: 更新取得・インストール
② の検証は①完了後に実施。FB 受信の SEC-01 は iPhone アプリでも同様に Bearer 付き POST となる想定。
7. 図の読み分け早見表
知りたいこと 見る図
MS-1〜3 の当時の FB の流れ §1 旧
いまの FB(鍵付き) §2 現行
HTTPS だけでは足りなかった理由 §0b 、§3
PAT と API キーの違い §2 + K-31 §4
月次 dispatch → APK §4
§6 機能・関数定義
検証対象コンポーネントの機能一覧。実装済み項目はパス・配置を具体化済み。
1. スタブモバイルアプリ(C-01)
機能 ID 機能名 概要 入出力 フェーズ
F-01
画像取得
ギャラリーから画像を選択(image_picker)
出力: 画像ファイルパス
① 実装済み
F-02
ダミー推論
DummyInference.generate() — ラベル A/B/C、スコア 0.75〜0.99
出力: {"label","score"}
① 実装済み
F-03
FB 送信
FeedbackClient.send() → POST /api/v1/feedback
デフォルト URL: https://api.dammy-otoko.com
① 実装済み
2. Web API(C-03)
機能 ID 関数 / エンドポイント 概要 フェーズ
F-10
POST /api/v1/feedback
FB データを受信し Storage / DB に保存
①
F-11
GET /api/v1/health
ヘルスチェック(Tunnel 疎通確認用)
①
3. AI Worker スタブ(C-06)
機能 ID 関数(予定) 概要 フェーズ
F-20
run_training_stub()
学習処理のスタブ。モデルファイルをコピーして新版本を生成
①
F-21
trigger_github_dispatch()
学習完了後に GitHub Actions を起動
①
4. GitHub Actions(C-07)
機能 ID ワークフロー 概要 フェーズ
F-30
android-build.yml
Flutter APK ビルド、artifact 出力
①
F-31
ios-build.yml
iOS ビルド、TestFlight 配布
②
5. 外部 API 呼び出し
機能 ID API 呼び出し元 概要
F-40
GitHub repos/.../dispatches
AI Worker
モデル更新完了を CI に通知