Skip to content

receiver: inbox ログイン + CORS 制限で公開デプロイをハードニングする (#43 PR-2) - #103

Merged
kosako merged 2 commits into
mainfrom
feat/43-inbox-auth-cors
Jul 5, 2026
Merged

receiver: inbox ログイン + CORS 制限で公開デプロイをハードニングする (#43 PR-2)#103
kosako merged 2 commits into
mainfrom
feat/43-inbox-auth-cors

Conversation

@kosako

@kosako kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner

概要

Issue #43(公開デプロイ時の receiver ハードニング)の PR-2。確定設計(2026-07-05 壁打ち)に沿った挙動変更の本体です。PR-1(#102、route table 純リファクタ)の上に載せています。

変更内容

inbox ブラウザ認証(ログインフォーム + HMAC cookie)

  • GET /login(フォーム)/ POST /login(検証 + cookie 発行)/ POST /logout を追加
  • セッション cookie は <expiry>.<HMAC-SHA256(RECEIVER_TOKEN, expiry)> の派生値(有効期限 7 日、HttpOnly、SameSite=Lax、publicBaseUrlhttps:// なら Secure)。生 token はブラウザに保存されず、token ローテで全端末即失効
  • node:crypto のみで zero-dep 維持

Bearer OR cookie(F-1 解消)

  • 操作系(import / status / delete / github-issue)と閲覧系(inbox / feedback.json / screenshots)の全 endpoint が Bearer または cookie を受ける
  • inbox の same-origin fetch には HttpOnly cookie が自動で乗るため、inbox.js は無改修で「RECEIVER_TOKEN 設定時に inbox 全操作が 401 で壊れる」(ARCH-REVIEW F-1 🔴)を解消
  • curl / API の Bearer 運用は従来通り
  • route table の auth kind は none / protected(401)/ page(未認証ブラウザは /login へ 303)

CORS の ingest 限定 + allowlist

  • CORS ヘッダーを出すのは POST /feedback(route table の cors: true)のみ。inbox・操作系・screenshot は同一オリジン利用のため CORS 自体を返さない(認証と二重の防壁)
  • ALLOWED_ORIGINS env / allowedOrigins config で許可 origin を完全一致制限。JSON POST は必ず preflight されるため、リスト外 origin のブラウザ投稿は本体 POST 前に遮断
  • 未設定は従来通り * + 起動ログに警告(ゼロ設定ローカル互換)

付随

  • 受信 feedback に received: { origin, originAllowed } を保存(弱シグナル。偽装不能な ingest key は Widget→Receiver 取り込みのペア認証と受信データの安全な取り扱い #44)。import bundle からの持ち込みは strip
  • 認証有効時、GitHub issue body の screenshot は [Open screenshot] リンクのみに退化(camo が cookie を送れないため)
  • inbox / login レスポンスに CSP(default-src 'none' ベース)と nosniff
  • README: RECEIVER_TOKEN / ALLOWED_ORIGINS の記述更新 + 「公開デプロイ(EC2 など)」節を新設
  • 起動ログ: 設定サマリ(auth / CORS 状態を含む)を先に出し、listening を最終行に

テスト

npm run check 全 100 件パス(新規 4 + 既存認証テスト更新 1 を含む)。実機スモーク済み: 未認証 inbox → /login 303 / 誤 token 401 / 正 token で cookie 発行 → inbox 200 / Bearer read 200 / allowlist echo + Vary / リスト外 origin に CORS ヘッダーなし / inbox に CORS なし / received メタデータ / logout で cookie 失効 / 405 + Allow。

Closes #43

🤖 Generated with Claude Code

https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau

Issue #43 の確定設計(2026-07-05 壁打ち)に沿った挙動変更の本体。

- inbox にログインフォーム(GET/POST /login、POST /logout)を追加。
  セッションは HMAC 派生 cookie(<expiry>.<HMAC(RECEIVER_TOKEN, expiry)>、
  7 日、HttpOnly、SameSite=Lax、publicBaseUrl が https なら Secure)で、
  生 token はブラウザに置かない。token 変更で全セッション即失効
- 操作系・閲覧系(inbox / feedback.json / screenshots)とも
  Bearer OR cookie を受ける。same-origin fetch は HttpOnly cookie が
  自動で乗るため inbox.js は無改修 → RECEIVER_TOKEN 設定時に inbox の
  全操作が 401 になる問題(ARCH-REVIEW F-1)を解消
- CORS を ingest(POST /feedback)のみに限定し、ALLOWED_ORIGINS /
  allowedOrigins の許可 origin 完全一致に制限。未設定は従来通り * +
  起動ログ警告。inbox・操作系・screenshot は CORS ヘッダー自体を返さない
- 受信 feedback に received: { origin, originAllowed } を保存
  (#44 の ingest key までの弱い出所シグナル)
- 認証有効時は GitHub issue body の screenshot 埋め込みをリンクのみに退化
  (GitHub camo は cookie を送れないため)
- inbox / login レスポンスに CSP と nosniff を付与
- README に公開デプロイ節(reverse proxy / HTTPS 終端 / secrets env 注入)

起動ログは設定サマリを先に出し「listening」を最終行に変更(起動完了の
シグナルとしての意味を明確化し、テストからサマリを検証可能にする)。

Closes #43

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
@kosako

kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

🔍 レビュー依頼(→ Codex)

  • 観点: 公開デプロイ時の receiver ハードニング(認証・CORS 制限) #43 PR-2(挙動変更の本体)。重点:
    • 認証境界の正しさ: HMAC cookie の検証(期限・署名・timing-safe 比較)、Bearer OR cookie の適用漏れ(route table の auth 宣言と実際の保護対象の一致)、token 未設定時のゼロ設定互換
    • CORS の縮約による退行: ingest 以外から CORS ヘッダーを外したことによる意図しない遮断、allowlist の origin 一致(正規化・trailing slash)、preflight(OPTIONS)の順序
    • セッション cookie の安全性: 生 token 非露出、Secure 属性の条件、logout の失効
    • 既存契約の維持: RECEIVER_TOKEN 未設定時の全挙動、curl の Bearer 運用、405 + Allow、rate limit の順序
    • production-rail review lens(フォールバック握り潰し・契約変更の整合・デッドコード)
  • ランク: 🔴 must(merge 前必須)/ 🟡 should(推奨・対応は依頼元判断)/ ⚪ nit(任意)
  • 結果はこの PR にコメントで返します

pathname だけで判定していたため GET /feedback などの 405 応答にも
Access-Control-Allow-Origin が付いていた(Codex レビュー 🟡 指摘)。
method も一致条件に加え、405 では CORS を出さない回帰テストを追加。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau
@kosako

kosako commented Jul 5, 2026

Copy link
Copy Markdown
Owner Author

📋 レビュー結果(by Codex GPT-5.5)

判定: ✅ merge 可(must 0 件) — 🟡 should 1 件(下記、対応済み)/ ⚪ nit 0 件

🔴 must

なし

🟡 should

  • server/receive.js:256 — CORS 判定が pathname のみのため、GET /feedback など POST /feedback 以外の unsupported method の 405 応答にも Access-Control-Allow-Origin が付く。実害は 405 JSON の露出程度で認証境界は破らないが、「CORS は ingest の POST /feedback のみ」という契約と少しズレる。req.method === "OPTIONS" || req.method === route.method を条件に加えるべき。

⚪ nit

なし

レビュアーの検証範囲(要旨)

route table / dispatch / HMAC cookie 検証 / CORS / login・logout / screenshot・feedback.json 保護を diff と実装の突き合わせで確認。inbox.js の same-origin fetch が cookie 認可で動くこと、テストの認証・cookie・CORS・405/OPTIONS/rate limit 回帰を実装と照合。read-only sandbox のためテスト実行は依頼元がローカルで代替(npm run check 全 100 件パス)。


依頼元対応

  • 🟡 は妥当と判断し 960c5db で対応済み: method を CORS 適用条件に追加(preflight の OPTIONS は許容)し、405 応答で CORS ヘッダーが出ない回帰テストを追加。npm run check 全 100 件パス。

@kosako
kosako marked this pull request as ready for review July 5, 2026 11:37
@kosako
kosako merged commit f5a21a4 into main Jul 5, 2026
1 check passed
@kosako
kosako deleted the feat/43-inbox-auth-cors branch July 5, 2026 11:37
kosako added a commit that referenced this pull request Jul 5, 2026
#103(inbox ログイン + CORS 制限)・#104(healthz / graceful shutdown /
設定警告)・#106(ingest key)で README.md に入れた receiver 契約の変更を
英語版にミラーする:

- RECEIVER_TOKEN の説明を「閲覧系も保護 + inbox ログイン(HMAC cookie)」に更新
- INGEST_KEYS / ALLOWED_ORIGINS の項目を追加
- healthz・graceful shutdown・invalid setting warning を追記
- Public Deployment (EC2 etc.) 節を新設
- Init options に ingestKey を追加、Current Boundary の Auth 行を更新

Closes #105


Claude-Session: https://claude.ai/code/session_01LRdDjwxbAAaTMwwnzipkau

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

公開デプロイ時の receiver ハードニング(認証・CORS 制限)

1 participant