コンテンツにスキップ

セキュリティ証跡

概要

本文書は Ritsubi E-Commerce のセキュリティ対策状況を記録する。継続的に更新し、脆弱性管理の証跡とする。


脆弱性スキャン結果

Trivy スキャン(2026-05-29 実施)

Scanner: Trivy vX.X (vulnerability scanner)
対象: リポジトリルート(除外: apps/wordpress-cms/vendor, node_modules 直下)
重篤度 件数 プロダクション影響
CRITICAL 0 なし
HIGH 14 なし(後述)
MEDIUM 0 なし
LOW 0 なし

HIGH 脆弱性の内訳

CVE パッケージ 検出箇所 プロダクション影響 対応方針
CVE-2026-35209 defu 6.1.4 apps/storefront/.npm-cache/_npx/.../package-lock.json なし — ローカルキャッシュのみ キャッシュクリアで除去
CVE-2026-26996 / CVE-2026-27903 / CVE-2026-27904 minimatch 3.1.2 apps/storefront/.npm-cache/_npx/.../package-lock.json なし — ローカルキャッシュのみ キャッシュクリアで除去
CVE-2026-33671 picomatch 2.3.1 / 4.0.3 apps/storefront/.npm-cache/_npx/.../package-lock.json なし — ローカルキャッシュのみ キャッシュクリアで除去
CVE-2025-68429 / CVE-2026-27148 storybook 10.0.0 apps/storefront/.npm-cache/_npx/.../package-lock.json なし — ローカルキャッシュのみ キャッシュクリアで除去
CVE-2025-64756 glob 10.4.5 @zenuml/core/bun.lock(ドキュメントツール) なし — ドキュメント生成ツールのみ 次回 @zenuml/core アップデート時に追従
CVE-2026-4800 lodash 4.17.21 @zenuml/core/bun.lock(ドキュメントツール) なし — ドキュメント生成ツールのみ 次回 @zenuml/core アップデート時に追従

結論: 現時点でプロダクション影響のある脆弱性は 0 件。

次回スキャン

定期スキャンは pnpm audit を CI に組み込み、CRITICAL/HIGH の新規検出時はアラートを出すことを推奨する(REQ-032 として追跡)。


暗号化状態

通信暗号化

通信経路 プロトコル 証明書管理 状態
顧客 ↔ Storefront HTTPS / TLS 1.3 Cloudflare 自動管理 ✅ 有効
顧客 ↔ Vendure API HTTPS / TLS 1.3 Fly.io 自動管理 ✅ 有効
顧客 ↔ WordPress HTTPS / TLS 1.3 Let's Encrypt(VPS) ✅ 有効
Storefront ↔ Vendure API HTTPS Fly.io 自動管理 ✅ 有効
Vendure ↔ PostgreSQL TLS(Fly 内ネットワーク) Fly.io 自動管理 ✅ 有効
Vendure ↔ Redis TLS(Fly 内ネットワーク) Fly.io 自動管理 ✅ 有効

保存データの暗号化

データ 暗号化方式 管理場所 状態
PostgreSQL ストレージ Fly.io Volume(OS レベル暗号化) Fly.io ✅ 有効
R2 バケット(メディア) Cloudflare R2 自動暗号化 Cloudflare ✅ 有効
Secrets / API キー AWS Secrets Manager AWS ✅ 有効
顧客パスワード bcrypt ハッシュ(Vendure 標準) PostgreSQL ✅ 有効

決済関連の暗号化

SB Payment Service は TLS 1.2 以上を要求する。

継続追跡事項(SHA-1 リスク): SB Payment の一部サーバー証明書に SHA-1 が含まれる可能性がある。

  • リスク: SHA-1 署名の証明書は段階的に廃止中(ブラウザ互換性問題の可能性)
  • 現状: SB Payment 側の問題であり、当社からの直接制御は困難
  • 対応: SB Payment への定期的な証明書更新確認、ブラウザコンソールエラーの Sentry 監視で検知

アクセス制御

管理画面

対象 認証方式 アクセス制御 状態
Vendure Dashboard(React) Vendure ネイティブ認証(JWT) ロールベース(Administrator / Operator) ✅ 有効
WordPress 管理画面 WordPress 認証 IP 制限 + 強パスワードポリシー ✅ 有効
Fly.io Dashboard Fly.io 認証(SSO) GitHub Org メンバーのみ ✅ 有効
Cloudflare Dashboard Cloudflare 認証(SSO) GitHub Org メンバーのみ ✅ 有効

API エンドポイント

エンドポイント 認証 レート制限 入力検証 状態
Storefront /api/auth-login 不要 (login mutation を内部 proxy) Cloudflare Workers Rate Limit binding (AUTH_LOGIN_RATE_LIMIT, IP/60s/10req) same-origin 強制 + 仮 password 12 文字 ✅ 有効
Storefront API(公開・その他) 不要(公開データ) Cloudflare WAF same-origin 強制 ✅ 有効
Vendure Shop API セッションまたは JWT Cloudflare WAF (shop-api 全体 rate limit は Issue #840 で追加予定) Origin validator (fail-closed in prod) + GraphQL depth limit (max 12) ✅ 有効
Vendure Admin API JWT(管理者のみ) IP + Cloudflare WAF Origin validator + HardenPlugin (complexity 2500 strict) ✅ 有効

実施済みセキュリティ対策(2026-05-24 audit-driven 改善)

第三者 audit (security-auditor / quality-engineer / performance-engineer 並列調査) で 検出された High 案件に対する正面対応の記録。本セクションは「なぜそうしたか」を 専門観点で明示し、将来の見直し基準を示す。

  • 対応ファイル:
  • apps/vendure-server/src/config/auth-options.ts
  • apps/vendure-server/src/middleware/origin-validator.middleware.ts (新規)
  • apps/vendure-server/src/middleware/origin-validator.middleware.spec.ts (新規, 7 ケース)
  • apps/vendure-server/src/config/api-options.ts
  • 変更内容:
  • 旧 production sameSite: "strict" → 全環境 "lax" に統一
  • OriginReferer 順で CORS allowlist と照合する middleware を apiOptions.middleware の先頭に配線
  • SAFE method (GET/HEAD/OPTIONS) は素通し、bypassRoutes: ["/payments/sbps", "/webhooks"] で server-to-server callback を除外
  • allowlist 空時は fail-safe で warn + passthrough(env 設定漏れで production 全停止を回避)
  • 専門観点での判断根拠:
  • SameSite=strict が止めるのは「攻撃者サイトに誘導された被害者ブラウザの cross-site state-changing リクエストへの cookie 同梱」だが、Vendure の state-changing は (a) GraphQL POST、(b) Content-Type: application/json (CORS preflight 強制)、(c) @Allow permission check で三重防御済み
  • simple POST 経路 (<form> enctype:application/x-www-form-urlencoded) は admin/shop API に存在しない
  • 一方 SBPS cross-site redirect 後の session 喪失リスクは桁違いに高い → trade-off で lax が合理的
  • 補完として Origin validator を同時投入し、lax の attack surface を strict 相当に縮約
  • Rollout:
  • cookie の name / domain / path 変更なしのため既存 session 即時失効は発生しない見込み
  • rollback は auth-options.tssameSite 戻し再 deploy のみ(migration 不要)
  • staging 一巡 (SBPS 通常 + callback タイムアウト + 再決済) 確認後に production 適用
  • 詳細は docs/03-implementation/payments/sb-payment-link.md の deploy ガード節

1-b. Session 保持期間(sessionDuration

  • 対応ファイル: apps/vendure-server/src/config/auth-options.ts
  • 設定値:
  • staging / production: 7d
  • local dev: 30d
  • Redis session cache の ttl も同じ値(dev 30 日 / prod 7 日)で揃える
  • 適用範囲: Vendure Shop API / Admin API いずれも同一。Storefront (Vite + TanStack Router on Cloudflare Workers) は独自の cookie maxAge を設定せず、Vendure 発行 session cookie の expiry に追従する。
  • 判断根拠:
  • 7 日は B2B ECで「平日ログインしたまま週末を跨いで業務再開」を許容しつつ、PC 共用や端末紛失時の暴露窓を 1 週間以内に抑える妥協点
  • dev で 30 日にしているのは開発者の再ログイン頻度を下げるためで、IS_DEV ガードで本番に漏れない
  • 変更時の手順: staging で一巡確認 → production 反映。cookie 名・domain・path は変えないため既存 session の即時失効は発生しない。

2. 仮 password 強度向上 (8 → 12 文字)

  • 対応ファイル:
  • packages/domain/src/rules/customer.ts:49 TEMPORARY_LOGIN_PASSWORD_LENGTH = 12
  • packages/domain/src/rules/customer.spec.ts 範囲 assertion (>=12) へ
  • 変更前: 8 文字 × alphabet 47 → エントロピー ~44.6 bits
  • 変更後: 12 文字 × alphabet 47 → エントロピー ~66.7 bits(目標 64 bits 超え)
  • 背景: SMILE インポート (packages/plugins/src/system-integration/smile/services/csv-import-customer.processor.ts) で複数顧客に並列発行され、メール配布までの遅延を考慮するとオフライン brute-force 余地を縮める必要
  • alphabet 方針: 既存どおり記号と紛らわしい文字を除外(メール・口頭で共有可能性を維持)

3. GraphQL depth limit (max 12) を Shop API に導入

  • 対応ファイル:
  • apps/vendure-server/src/config/graphql-depth-limit.ts (新規)
  • apps/vendure-server/src/config/api-options.ts
  • 変更内容: 標準 graphql の validation rule のみで実装した always-on maxDepth = 12shopApiValidationRules に注入。introspection (__schema 等) は除外して tooling 互換を維持
  • 判断根拠:
  • 新規 npm 依存(graphql-armor / graphql-depth-limit)追加は production リスクを増やすため避け、標準 API のみで完結
  • 既存 HardenPlugin (apps/vendure-server/src/config/plugins.ts:127) は env flag VENDURE_HARDEN_PLUGIN_ENABLED でゲート。本 PR では env を変えず、depth limit 単独の防御を always-on で提供

4. AGENTS ガードレール追記

  • apps/vendure-server/AGENTS.md に 1 行:
  • 「session cookie は全環境 lax を維持、CSRF 防御は origin-validator.middleware.ts で補完、apiOptions.middleware 先頭への配線必須、server-to-server callback は bypassRoutes で除外」
  • Storefront Worker → Vendure 呼び出しの upstream origin 規約も同 AGENTS に追記(Worker 入口でブラウザ起点 request の same-origin を検証し、Vendure へは VITE_PUBLIC_SITE_URL / SITE_URL の canonical origin を渡す)。

6. Production hardening(2026-05-27)

production 開始前 audit で検出された High / Medium 案件への正面対応。詳細は各 PR 参照。

6-a. Swagger /api-docs を production で既定無効化

  • 対応ファイル: apps/vendure-server/src/index.ts, packages/contract/src/validation/apps.ts
  • 変更内容: apiDocsEnabledenv.VENDURE_API_DOCS_ENABLED ?? env.NODE_ENV !== "production" に変更。production では Swagger UI / OpenAPI JSON を serve しない。
  • 判断根拠: System Integration の webhook spec を外部に露出させないため。production で一時的に有効化したい場合は env で明示 opt-in。

6-b. originValidator を production で fail-closed 化

  • 対応ファイル: apps/vendure-server/src/middleware/origin-validator.middleware.ts, apps/vendure-server/src/config/api-options.ts
  • 変更内容:
  • emptyAllowlistMode: "warn-and-pass" | "reject" option を追加し、production では "reject" を渡す。
  • production 起動時に allowlist が空なら process abort (CORS_ORIGIN / STOREFRONT_URL / ADMIN_URL のいずれかが必須)。
  • 判断根拠: AGENTS.md「黙った fail-open を避ける」原則。env 設定漏れによる CSRF 防御の silent disable を防ぐ。

6-c. HardenPlugin の production 必須化(SEC-005 解消)

  • 対応ファイル: apps/vendure-server/src/config/plugins.ts
  • 変更内容: production で VENDURE_HARDEN_PLUGIN_ENABLED が true でなければ起動時に throw。
  • 運用: Fly secret に VENDURE_HARDEN_PLUGIN_ENABLED=true を設定して deploy する。

6-d. Vendure 側 security header middleware 追加

  • 対応ファイル: apps/vendure-server/src/middleware/security-headers.middleware.ts (新規), apps/vendure-server/src/config/api-options.ts
  • 変更内容: 全レスポンスに X-Content-Type-Options, X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy を付与。production / staging では HSTS を強制。
  • 判断根拠: Storefront 側は既に applyStorefrontResponseSecurityHeaders で実装済みだが、Vendure 側 (admin-api / shop-api / system-integration) は無防備だった。

6-e. /api/auth-login の Cloudflare Rate Limit binding(SEC-004 解消)

  • 対応ファイル:
  • apps/storefront/src/worker-api/rate-limit.ts (新規)
  • apps/storefront/src/worker-api/handlers/auth.ts
  • apps/storefront/wrangler.toml (全 env に [[unsafe.bindings]] type = "ratelimit" を追加)
  • 変更内容: Cloudflare Workers Rate Limiting binding を使い、IP / 60s window / 10 req を上限に。binding 未設定の dev / ローカルでは素通り (fail-open)。
  • 判断根拠: KV ベース実装より低レイテンシ・low ops。Cloudflare 内蔵 sliding window counter で、同一アカウント内で namespace_id 別 (production=1101, preview=1102, staging=1103, mock=1104) によりカウンタ分離。

6-f. storefront logger の LOG_LEVEL filter

  • 対応ファイル: apps/storefront/src/lib/logger.ts, apps/storefront/src/env.schema.ts
  • 変更内容: STOREFRONT_LOG_LEVEL / LOG_LEVEL env で最小出力 level を制御。未設定時は production / staging で info 以上に絞り、Cloudflare Workers logs の signal/noise を改善。

5. Storefront Worker → Vendure の upstream origin 正規化(2026-05-25 更新)

  • 対応ファイル:
  • apps/storefront/src/worker-api/vendure-client.ts (buildVendureHeaders)
  • apps/storefront/src/worker-api/handlers/commerce.ts (proxyCommerceRequest)
  • 背景:
  • 上記 1. の origin-validator middleware 導入時、Storefront Worker → Vendure shop-api 経路の Origin 転送が抜けており、login mutation / カート操作など全 state-changing GraphQL が 403 Forbidden: missing Origin/Referer for state-changing request で落ちた(手動検証で発覚: test1@ritsubi-platform.com / 開発 password でログイン不可)。
  • 2026-05-25 に medical.ritsubi.co.jp alias からの production login と /commerce/shop-api が失敗した。原因は Worker が alias domain の Origin を Vendure へ渡し、Vendure allowlist と公開 alias 運用が drift したこと。
  • 修正方針 (採用): Worker 入口で browser の same-origin を検証し、Vendure へは canonical Storefront origin を渡す。
  • ブラウザ起点 → Worker で Origin / Referer が request URL と同一 origin であることを検証する。
  • Worker → Vendure → VITE_PUBLIC_SITE_URL / SITE_URL の canonical origin(production は https://order.ritsubi-platform.com)を Origin として送る。/commerce proxy では Referer も canonical に正規化する。
  • 二段防御: Worker 入口で alias を含む同一 origin を強制 → Vendure 側では Storefront canonical origin のみを server-to-server caller として検証する。
  • 却下した代替案:
  • 「公開 alias をすべて Vendure allowlist に追加する」: alias 追加・削除と Vendure deploy がずれるたびに再発する。
  • 「Vendure 側で内部 caller 専用 bypass header (shared secret)」: secret 管理コストが増え、SBPS bypassRoutes と二系統になる。canonical origin 正規化なら既存の allowlist 設計だけで一貫する。
  • 専門観点での判断根拠:
  • origin-validator は元々「ブラウザ起点 CSRF」を仮定するが、Cloudflare Worker が Storefront の同一 origin boundary になっているため、browser origin 検証は Worker 入口で完了させるのが責務境界として正しい。
  • alias domain を Vendure へ流さないことで、Storefront の公開入口追加と Vendure の CORS / origin allowlist が密結合しない。
  • SBPS 等の本物の server-to-server callback は bypassRoutes で従来通り除外されるため影響なし。

既知のセキュリティリスクと追跡

ID リスク 重篤度 現状 対応計画
SEC-001 SB Payment SHA-1 証明書の可能性 MEDIUM 監視中 SB Payment への確認・Sentry アラート設定
SEC-002 旧システムからの移行ユーザーパスワードの再設定 HIGH Issue #780 で追跡 パスワード再設定メール送信フロー実装(REQ-031 待ち)
SEC-003 管理者アカウントの定期的なパスワード更新ポリシー LOW 未実装 ポリシー文書化・Vendure Dashboard での通知
SEC-004 /api/auth-login の brute-force rate limit HIGH ✅ 実装済み (2026-05-27) Cloudflare Workers Rate Limiting binding AUTH_LOGIN_RATE_LIMIT (IP / 60s / 10 req) を apps/storefront/src/worker-api/handlers/auth.ts で適用。namespace_id は env 別
SEC-005 HardenPlugin (GraphQL complexity 2500 strict 運用) を production env で有効化 MEDIUM ✅ 実装済み apps/vendure-server/src/config/plugins.ts で production 起動時に VENDURE_HARDEN_PLUGIN_ENABLED=true を必須化し、maxQueryComplexity=2500 で過大 query を fail-closed する
SEC-006 Double-submit CSRF token (Dashboard ↔ Admin API) MEDIUM 未実装 Origin validator で代替防御中。本格対応は Dashboard 側 UI 改修 + worker header 付与が必要 → 別 PR
SEC-007 SBPS service log で response payload に PII (sps_cust_no 等) を含む JSON.stringify MEDIUM 既存実装 packages/plugins/src/payment-integration/sb-payment-link/sb-payment-link.service.ts:306,437,488,581 に専用 sanitizer を導入
SEC-008 ctx.activeUserId ?? "" の silent fail-open (points.shop.resolver.ts:22,34,43) MEDIUM 既存実装 ForbiddenError で fail-closed 化
SEC-009 SBPS callback の simulatorMode === true で amount 検証 skip MEDIUM 既存実装 production NODE_ENV で simulatorMode を起動時 reject
SEC-010 SMILE CSV import の upload size 制限不在 MEDIUM 既存実装 apiOptions.uploadMaxFileSize 明示 + processor 入口で行数 cap

変更履歴

日付 変更内容 担当
2026-05-29 初版作成(Trivy スキャン結果・暗号化状態・アクセス制御を記録) AI Agent
2026-05-24 audit-driven 改善: SameSite=lax 統一 + Origin validator middleware / 仮 password 強度 12 文字 / GraphQL depth limit / 追跡項目 SEC-004〜010 追加 AI Agent
2026-05-27 Production hardening: Swagger 既定無効化 / originValidator fail-closed / HardenPlugin fail-fast (SEC-005 解消) / Vendure security header middleware / /api/auth-login Rate Limit binding (SEC-004 解消) / storefront logger LOG_LEVEL filter AI Agent