373 lines
12 KiB
Markdown
373 lines
12 KiB
Markdown
# CertRemind 開発ガイド
|
|
|
|
## 概要
|
|
|
|
- アプリケーション名: CertRemind
|
|
- 目的: ユーザーが登録した Web サイトの TLS/SSL 証明書の有効期限を監視し、期限切れ前にアプリ内アラート、Webhook、Push 通知で知らせる。
|
|
- 現在の実装状況: `development_plan.md` のフェーズ 8 までの機能を実装済み。MVP の主要機能に加え、基本的な品質向上、テスト、運用準備も整備済み。
|
|
|
|
## 技術スタック
|
|
|
|
- Node.js v22
|
|
- pnpm
|
|
- Hono.js
|
|
- React.js / Vite
|
|
- Radix UI
|
|
- lucide-react
|
|
- PostgreSQL
|
|
- Docker Compose
|
|
- OpenSSL
|
|
- Argon2id
|
|
- TOTP: `otplib`
|
|
- Push 通知: `web-push`
|
|
- QR コード: `qrcode.react`
|
|
- テスト: Vitest
|
|
- Lint / Format: ESLint / Prettier
|
|
|
|
## 主要コマンド
|
|
|
|
```text
|
|
pnpm install
|
|
docker compose up -d postgres
|
|
pnpm dev
|
|
pnpm monitor:once
|
|
pnpm monitor:worker
|
|
pnpm lint
|
|
pnpm test
|
|
pnpm exec vite build
|
|
pnpm format
|
|
```
|
|
|
|
開発サーバー:
|
|
|
|
```text
|
|
Frontend: http://127.0.0.1:5173/
|
|
API: http://127.0.0.1:3000
|
|
```
|
|
|
|
## ディレクトリ構成
|
|
|
|
```text
|
|
db/schema.sql
|
|
README.md
|
|
public/push-sw.js
|
|
src/client
|
|
src/client/api/client.js
|
|
src/client/components
|
|
src/client/components/Toast.jsx
|
|
src/client/routes
|
|
src/client/styles/app.css
|
|
src/server
|
|
src/server/app.js
|
|
src/server/index.js
|
|
src/server/config/env.js
|
|
src/server/db/pool.js
|
|
src/server/middleware
|
|
src/server/modules
|
|
src/server/jobs/monitorCertificates.js
|
|
src/server/jobs/monitorWorker.js
|
|
src/server/utils/logger.js
|
|
tests/apiSecurity.test.js
|
|
tests/monitoring.test.js
|
|
tests/urlPolicy.test.js
|
|
```
|
|
|
|
## 環境変数
|
|
|
|
`.env.example` を参照すること。
|
|
|
|
主な項目:
|
|
|
|
- `NODE_ENV`
|
|
- `PORT`
|
|
- `DATABASE_URL`
|
|
- `COOKIE_SECRET`
|
|
- `VAPID_PUBLIC_KEY`
|
|
- `VAPID_PRIVATE_KEY`
|
|
- `VAPID_SUBJECT`
|
|
- `OPENSSL_PATH`
|
|
|
|
補足:
|
|
|
|
- `OPENSSL_PATH` 未設定時は `openssl` を使う。
|
|
- Windows では Git 付属の `openssl.exe` を自動検出する実装がある。
|
|
- VAPID private key が未設定の場合、Push 実送信は失敗として `delivery_result` に記録される。
|
|
|
|
## データベース
|
|
|
|
DDL は `db/schema.sql` に保管している。
|
|
|
|
実装済みテーブル:
|
|
|
|
- `users`
|
|
- `user_totp`
|
|
- `sessions`
|
|
- `sites`
|
|
- `notification_methods`
|
|
- `site_alert_conditions`
|
|
- `alert_history`
|
|
|
|
設計メモ:
|
|
|
|
- 主キーは UUID。
|
|
- 全テーブルに `created_at` / `updated_at` を持つ。
|
|
- `updated_at` はトリガーで更新する。
|
|
- `sites` は最新の証明書期限、確認日時、取得失敗内容を保持する。
|
|
- ユーザー関連データは `users` 削除を起点に CASCADE で削除する。
|
|
- サイト削除時、通知条件は CASCADE で削除する。
|
|
- アラート履歴の `site_id` はサイト削除時に `NULL` へ変更する。
|
|
- アラート重複抑制は `alert_history.dedupe_key` を使う。
|
|
|
|
注意:
|
|
|
|
- `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。
|
|
|
|
## 実装済み API
|
|
|
|
```text
|
|
GET /api/health
|
|
|
|
GET /api/auth/csrf
|
|
POST /api/auth/register
|
|
POST /api/auth/login
|
|
POST /api/auth/logout
|
|
GET /api/auth/me
|
|
|
|
GET /api/sites
|
|
POST /api/sites
|
|
GET /api/sites/:siteId
|
|
PATCH /api/sites/:siteId
|
|
DELETE /api/sites/:siteId
|
|
|
|
GET /api/sites/:siteId/settings
|
|
PUT /api/sites/:siteId/settings
|
|
DELETE /api/sites/:siteId/settings
|
|
|
|
GET /api/alerts
|
|
PATCH /api/alerts/:alertId/read
|
|
DELETE /api/alerts/:alertId
|
|
|
|
GET /api/notification-methods
|
|
POST /api/notification-methods/webhooks
|
|
PATCH /api/notification-methods/webhooks/:methodId
|
|
DELETE /api/notification-methods/webhooks/:methodId
|
|
POST /api/notification-methods/push-subscriptions
|
|
|
|
GET /api/account
|
|
PATCH /api/account/profile
|
|
PATCH /api/account/password
|
|
POST /api/account/totp/setup
|
|
POST /api/account/totp/verify
|
|
DELETE /api/account/totp
|
|
DELETE /api/account
|
|
```
|
|
|
|
## 実装済み画面
|
|
|
|
認証前:
|
|
|
|
- 登録画面
|
|
- ログイン画面
|
|
- 2 段階認証コード入力
|
|
|
|
認証後:
|
|
|
|
- URL ルーティング
|
|
- `/sites`
|
|
- `/sites/:siteId/settings`
|
|
- `/alerts`
|
|
- `/notifications`
|
|
- `/account`
|
|
- `/login`
|
|
- `/register`
|
|
- 直リンク、初期読込み、ブラウザバック / フォワードに対応
|
|
- 未認証で保護画面 URL を開いた場合はログイン画面を表示し、ログイン後に元の画面へ復帰する
|
|
- 左サイドメニュー
|
|
- PC 幅では展開表示
|
|
- 720px 以下ではアイコンのみの畳み表示
|
|
- サイト一覧、アラート履歴、通知方法、アカウント、ログアウトに対応
|
|
- 共通トースト通知
|
|
- 認証後画面の正常メッセージ、エラーメッセージを右上に表示
|
|
- 5 秒後の自動非表示と手動クローズに対応
|
|
- ログイン / 登録画面のエラーはインライン表示を維持
|
|
- サイト一覧
|
|
- サイト追加
|
|
- 最新監視結果に基づく証明書期限表示
|
|
- 確認ダイアログ付きサイト削除
|
|
- サイト設定
|
|
- エイリアス編集
|
|
- 通知タイミング設定
|
|
- 時間 / 日 / 週間の単位指定
|
|
- 複数タイミングの追加
|
|
- 確認ダイアログ付き通知タイミング削除
|
|
- アプリ内アラート必須表示
|
|
- Webhook 選択
|
|
- Push 通知フラグ設定
|
|
- 確認ダイアログ付き設定削除
|
|
- アラート一覧
|
|
- サイト絞り込み
|
|
- アラート種類絞り込み
|
|
- 開始日時 / 終了日時絞り込み
|
|
- 既読更新
|
|
- 確認ダイアログ付き履歴削除
|
|
- 通知方法管理
|
|
- Webhook 登録
|
|
- Webhook 編集
|
|
- 確認ダイアログ付き Webhook 削除
|
|
- ブラウザ Push 通知の許可状態表示
|
|
- VAPID public key がある場合の Push 購読登録
|
|
- アカウント設定
|
|
- 表示名更新
|
|
- ダイアログでのパスワード更新
|
|
- パスワード更新後の全セッション無効化
|
|
- ステップ式ポップアップでの 2 段階認証セットアップ
|
|
- 2 段階認証 QR コード表示
|
|
- シークレット文字列表示
|
|
- 確認ダイアログ付き 2 段階認証解除
|
|
- 確認ダイアログ付きアカウント削除
|
|
|
|
## 入力検証
|
|
|
|
サーバー側:
|
|
|
|
- Zod でリクエストを検証する。
|
|
- クライアントから送られる `user_id` は信用しない。
|
|
- 認証済み API はセッションのユーザー ID を使う。
|
|
- SQL はプレースホルダを使う。
|
|
|
|
クライアント側:
|
|
|
|
- 登録 / ログイン、サイト、Webhook、アラート絞り込み、アカウント設定で入力必須チェックと形式チェックを実装済み。
|
|
- クライアント側検証は UX 用であり、サーバー側検証を省略してはいけない。
|
|
|
|
## 認証・セッション
|
|
|
|
- パスワードは Argon2id でハッシュ化する。
|
|
- セッションは `sessions` テーブルで管理する。
|
|
- セッション Cookie は HttpOnly / SameSite=Lax。
|
|
- 本番環境では Secure Cookie を有効化する。
|
|
- CSRF トークンを状態変更 API で必須にする。
|
|
- パスワード更新時は対象ユーザーの全セッションを削除し、現在の Cookie も削除する。
|
|
- TOTP が有効なユーザーはログイン時に OTP 検証が必須。
|
|
|
|
## サイト管理
|
|
|
|
- サイト URL は HTTPS のみ許可する。
|
|
- URL は `normalizeHttpsUrl` で正規化する。
|
|
- `localhost`、private IPv4、loopback IPv4 は拒否する。
|
|
- サイト API は常にセッションユーザーの所有データのみ扱う。
|
|
- サイト設定の通知タイミングは内部的に時間単位で保存する。
|
|
- 通知タイミングは 1 時間以上、17520 時間以内。
|
|
- 同じサイトに同じ通知タイミングを重複登録できない。
|
|
|
|
## アラート履歴
|
|
|
|
- アラート履歴は `alert_history` に保存する。
|
|
- アラート一覧はログインユーザーの履歴のみ返す。
|
|
- 既読更新、削除もログインユーザーの履歴のみ対象にする。
|
|
- 絞り込み条件:
|
|
- サイト
|
|
- アラート種類
|
|
- 開始日時
|
|
- 終了日時
|
|
- アラート重複抑制には `dedupe_key` を使う。
|
|
|
|
## 通知方法
|
|
|
|
Webhook:
|
|
|
|
- HTTPS のみ許可する。
|
|
- `normalizeHttpsUrl` を通し、localhost / private IPv4 / loopback IPv4 を拒否する。
|
|
- Slack 互換 Webhook として送信する。
|
|
- 更新・削除はログインユーザーの通知方法のみ対象。
|
|
|
|
Push:
|
|
|
|
- ブラウザ Push 購読情報を `notification_methods` に保存する。
|
|
- endpoint は HTTPS のみ許可する。
|
|
- Service Worker は `public/push-sw.js`。
|
|
- VAPID key は環境変数で管理する。
|
|
|
|
## 証明書監視ジョブ
|
|
|
|
一回実行:
|
|
|
|
```text
|
|
pnpm monitor:once
|
|
```
|
|
|
|
定期実行 worker:
|
|
|
|
```text
|
|
pnpm monitor:worker
|
|
```
|
|
|
|
処理内容:
|
|
|
|
- 登録サイトを取得する。
|
|
- OpenSSL で証明書期限を取得する。
|
|
- 成功時は `sites.certificate_expires_at` / `certificate_checked_at` を更新し、取得失敗内容をクリアする。
|
|
- 失敗時は `sites.certificate_checked_at` / `certificate_check_error` を更新し、既存の期限値は残す。
|
|
- サイトごとの通知条件を評価する。
|
|
- 条件一致時にアラート履歴を作成する。
|
|
- Webhook / Push 通知を送信する。
|
|
- 証明書取得失敗も `certificate_check_failed` としてアラート化する。
|
|
- サイト単位の失敗で全体を止めない。
|
|
- 外部通信を伴う監視処理は並列数を制限する。
|
|
- Webhook / Push の送信失敗は `delivery_result` に記録する。
|
|
- 監視ジョブの開始、終了、失敗は構造化ログで出力する。
|
|
- `pnpm monitor:worker` は 1 時間ごとに監視ジョブを実行する長時間起動プロセス。
|
|
|
|
注意:
|
|
|
|
- サンドボックス内では外部 TLS 接続が `Permission denied` になる場合がある。その場合は権限付き実行が必要。
|
|
- 定期実行は `pnpm monitor:worker` で提供済み。運用要件に応じて cron やタスクスケジューラから `pnpm monitor:once` を実行する構成も選択できる。
|
|
|
|
## セキュリティ方針
|
|
|
|
- リクエスト改ざんに注意する。
|
|
- クライアントから送信されてきたデータを信頼しない。
|
|
- CSRF トークンを使用する。
|
|
- パスワードを平文保存しない。
|
|
- SQL インジェクションを避けるためプレースホルダを使う。
|
|
- 認可はセッションユーザーを基準に判定する。
|
|
- URL ルーティングは表示状態の復元用であり、認可は必ず API 側のセッションユーザー基準で判定する。
|
|
- 未認証時の復帰先 URL はアプリ内の許可済みパスのみ扱い、外部 URL へのリダイレクトに使わない。
|
|
- 1 リクエストがサーバー全体の処理をブロックしないようにする。
|
|
- 外部通信はタイムアウトや並列数制限を設定する。
|
|
- 削除操作には確認ダイアログを置く。
|
|
- API の未捕捉エラーは構造化ログで記録する。
|
|
|
|
## テスト・運用準備
|
|
|
|
- `tests/urlPolicy.test.js` で URL 正規化と基本的な SSRF 対策を検証する。
|
|
- `tests/apiSecurity.test.js` で CSRF、認証必須、セッションユーザー基準の認可、Webhook URL 検証、通知条件の所有者確認を検証する。
|
|
- `tests/monitoring.test.js` で証明書監視成功 / 失敗時の DB 更新、アラート作成、通知呼び出しを検証する。
|
|
- `README.md` に開発環境起動手順、検証コマンド、DB 注意点、環境変数一覧を記載済み。
|
|
- サンドボックス環境では Vite / Vitest の設定解決で権限付き実行が必要になる場合がある。
|
|
|
|
## 既知の強化候補
|
|
|
|
- API 単体テスト / 統合テストをさらに増やす。
|
|
- ログイン試行回数制限を追加する。
|
|
- SSRF 対策として DNS 解決後の IP チェックを追加する。
|
|
- IPv6 private / link-local / unique local address の検証を追加する。
|
|
- セッションローテーションを追加する。
|
|
- E2E テストを追加する。
|
|
|
|
## 開発時の注意
|
|
|
|
- `development_status.md` は作業後に更新する。
|
|
- DB 変更を行う場合は `db/schema.sql` も更新する。
|
|
- API を追加したら、認証、CSRF、認可、入力検証を確認する。
|
|
- UI の削除操作には確認ダイアログを追加する。
|
|
- 既存の左サイドメニューとレスポンシブ挙動を崩さない。
|
|
- フロントエンドの入力チェックを追加しても、サーバー側検証を必ず維持する。
|
|
- 変更後は少なくとも以下を実行する。
|
|
|
|
```text
|
|
pnpm lint
|
|
pnpm test
|
|
pnpm exec vite build
|
|
```
|