390 lines
14 KiB
Markdown
390 lines
14 KiB
Markdown
# CertRemind 開発進捗
|
|
|
|
最終更新: 2026-05-25
|
|
|
|
## 現在の実装状況
|
|
|
|
`development_plan.md` のフェーズ 8 までを中心に、MVP の土台、サイト設定、アラート履歴、通知方法管理、証明書監視ジョブ、アカウント設定、品質向上と運用準備を実装済み。
|
|
|
|
### 完了済み
|
|
|
|
- Hono.js ベースの API サーバー構成
|
|
- React / Vite ベースのフロントエンド構成
|
|
- 本番相当 / 開発用に分離した Docker Compose 構成
|
|
- Docker Compose での Web/API アプリと監視 worker 起動
|
|
- 環境変数サンプル `.env.example`
|
|
- DB 論理定義に基づく `db/schema.sql`
|
|
- DB 接続モジュール
|
|
- CSRF トークン発行と状態変更 API での検証
|
|
- Cookie セッション管理
|
|
- 認証必須ミドルウェア
|
|
- 登録 API
|
|
- ログイン API
|
|
- ログアウト API
|
|
- 現在ユーザー取得 API
|
|
- サイト一覧 API
|
|
- サイト一覧 API で最新の証明書発行元・発行日時・期限・確認日時・取得失敗状態を返却
|
|
- サイト登録 API
|
|
- サイト登録時の証明書発行元・発行日時・期限の初期取得
|
|
- サイト詳細 API
|
|
- サイトエイリアス更新 API
|
|
- サイト削除 API
|
|
- サイト設定取得 API
|
|
- サイト設定保存 API
|
|
- サイト設定削除 API
|
|
- アラート一覧 API
|
|
- アラート既読更新 API
|
|
- アラート履歴削除 API
|
|
- サイト、日時、アラート種類によるアラート絞り込み API
|
|
- 通知方法一覧 API
|
|
- Webhook 登録 API
|
|
- Webhook 更新 API
|
|
- Webhook 削除 API
|
|
- Push 購読情報登録 API
|
|
- OpenSSL による証明書発行元・発行日時・期限取得処理
|
|
- 監視ジョブで取得した最新の証明書発行元・発行日時・期限・確認日時・取得失敗状態をサイトに保存
|
|
- サイトごとの通知条件評価
|
|
- 条件一致時のアラート履歴作成
|
|
- 証明書取得失敗時のアラート履歴作成
|
|
- Webhook 通知送信処理
|
|
- Push 通知送信処理
|
|
- 二重送信を防ぐ `dedupe_key` 利用
|
|
- 並列数を制限した監視ジョブ
|
|
- 監視ジョブの一回実行スクリプト
|
|
- 1 時間ごとに監視ジョブを実行する Node worker
|
|
- アカウント情報取得 API
|
|
- 表示名更新 API
|
|
- パスワード更新 API
|
|
- パスワード更新時の全セッション無効化
|
|
- TOTP セットアップ API
|
|
- TOTP 検証・有効化 API
|
|
- TOTP 解除 API
|
|
- ログイン時の TOTP 検証
|
|
- アカウント削除 API
|
|
- URL 正規化と基本的な SSRF 対策
|
|
- 登録 / ログイン画面
|
|
- サイト一覧・登録・編集・削除画面
|
|
- サイト設定画面
|
|
- アラート一覧画面
|
|
- 通知方法管理画面
|
|
- アカウント設定画面
|
|
- ESLint / Prettier / Vitest / Vite 設定
|
|
- URL ポリシーの単体テスト
|
|
- API セキュリティ境界の統合テスト
|
|
- 証明書監視処理の単体テスト
|
|
- API 未捕捉エラーと監視ジョブの構造化ログ
|
|
- README に開発環境起動手順、Docker Compose 手順、環境変数一覧を追加
|
|
- AGENTS に Docker Compose 構成と loopback 公開方針を追加
|
|
- `.gitignore` の整備
|
|
|
|
## 作成・更新した主なファイル
|
|
|
|
- `package.json`
|
|
- `pnpm-lock.yaml`
|
|
- `pnpm-workspace.yaml`
|
|
- `Dockerfile`
|
|
- `.dockerignore`
|
|
- `docker-compose.yml`
|
|
- `docker-compose.dev.yml`
|
|
- `.env.example`
|
|
- `.gitignore`
|
|
- `README.md`
|
|
- `.prettierrc`
|
|
- `eslint.config.js`
|
|
- `vite.config.js`
|
|
- `index.html`
|
|
- `db/schema.sql`
|
|
- `src/server/app.js`
|
|
- `src/server/index.js`
|
|
- `src/server/config/env.js`
|
|
- `src/server/db/pool.js`
|
|
- `src/server/middleware/auth.js`
|
|
- `src/server/middleware/csrf.js`
|
|
- `src/server/modules/account/routes.js`
|
|
- `src/server/modules/alerts/routes.js`
|
|
- `src/server/modules/auth/routes.js`
|
|
- `src/server/modules/notificationMethods/routes.js`
|
|
- `src/server/modules/monitoring/certificate.js`
|
|
- `src/server/modules/monitoring/monitor.js`
|
|
- `src/server/modules/monitoring/notifications.js`
|
|
- `src/server/modules/sites/routes.js`
|
|
- `src/server/jobs/monitorCertificates.js`
|
|
- `src/server/utils/httpErrors.js`
|
|
- `src/server/utils/logger.js`
|
|
- `src/server/utils/urlPolicy.js`
|
|
- `src/client/main.jsx`
|
|
- `src/client/App.jsx`
|
|
- `src/client/api/client.js`
|
|
- `src/client/components/Field.jsx`
|
|
- `src/client/routes/AlertsView.jsx`
|
|
- `src/client/routes/AuthPanel.jsx`
|
|
- `src/client/routes/AccountView.jsx`
|
|
- `src/client/routes/NotificationMethodsView.jsx`
|
|
- `src/client/routes/SiteSettingsPanel.jsx`
|
|
- `src/client/routes/SitesView.jsx`
|
|
- `src/client/styles/app.css`
|
|
- `public/push-sw.js`
|
|
- `tests/urlPolicy.test.js`
|
|
- `tests/apiSecurity.test.js`
|
|
- `tests/monitoring.test.js`
|
|
|
|
## データベース
|
|
|
|
`db/schema.sql` に以下のテーブルを定義済み。
|
|
|
|
- `users`
|
|
- `user_totp`
|
|
- `sessions`
|
|
- `sites`
|
|
- `notification_methods`
|
|
- `site_alert_conditions`
|
|
- `alert_history`
|
|
|
|
補足:
|
|
|
|
- 主キーは UUID。
|
|
- 全テーブルに `created_at` / `updated_at` を付与。
|
|
- `sites` は最新の証明書発行元、発行日時、期限、確認日時、取得失敗内容を保持。
|
|
- `updated_at` 更新用トリガーを定義。
|
|
- ユーザー関連データは `ON DELETE CASCADE` を中心に設計。
|
|
- サイト削除時は通知条件も削除される。
|
|
- アラート履歴のサイト参照は `ON DELETE SET NULL`。
|
|
|
|
## 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
|
|
```
|
|
|
|
### 実装済みジョブ
|
|
|
|
```text
|
|
pnpm monitor:once
|
|
pnpm monitor:worker
|
|
```
|
|
|
|
### 未実装
|
|
|
|
```text
|
|
なし
|
|
```
|
|
|
|
## フロントエンド
|
|
|
|
### 実装済み画面
|
|
|
|
- 登録画面
|
|
- ログイン画面
|
|
- サイト一覧画面
|
|
- サイト追加
|
|
- 最新監視結果に基づく証明書期限表示
|
|
- 確認ダイアログ付きサイト削除
|
|
- ログアウト
|
|
- サイト設定画面
|
|
- エイリアス編集
|
|
- 証明書情報として発行元、発行日時、失効日時を表示
|
|
- 通知タイミング設定
|
|
- 時間 / 日 / 週間の単位指定
|
|
- 複数タイミングの追加・確認ダイアログ付き削除
|
|
- アプリ内アラート必須表示
|
|
- Webhook 選択
|
|
- プッシュ通知フラグ設定
|
|
- アラート一覧画面
|
|
- サイト絞り込み
|
|
- アラート種類絞り込み
|
|
- 開始日時 / 終了日時絞り込み
|
|
- 既読更新
|
|
- 確認ダイアログ付き履歴削除
|
|
- 通知方法管理画面
|
|
- Webhook 登録
|
|
- Webhook 編集
|
|
- 確認ダイアログ付き Webhook 削除
|
|
- ブラウザ Push 通知の許可状態表示
|
|
- VAPID public key がある場合の Push 購読登録
|
|
- アカウント設定画面
|
|
- 表示名更新
|
|
- ダイアログでのパスワード更新
|
|
- ステップ式ポップアップでの 2 段階認証セットアップ
|
|
- 2 段階認証 QR コード表示
|
|
- 確認ダイアログ付き 2 段階認証解除
|
|
- 確認ダイアログ付きアカウント削除
|
|
- 認証後画面共通の左サイドメニュー
|
|
- PC 幅では展開表示
|
|
- スマートフォンなど狭い幅ではアイコンのみの畳み表示
|
|
- サイト一覧、アラート履歴、通知方法、アカウント、ログアウトに対応
|
|
- 認証後画面の URL ルーティング
|
|
- サイト一覧、サイト設定、アラート履歴、通知方法、アカウントに個別 URL を付与
|
|
- 直リンク、初期読込み、ブラウザバック / フォワードに対応
|
|
- 未認証時はログイン後に元の保護画面へ復帰
|
|
- 認証後画面共通のトースト通知
|
|
- 正常メッセージとエラーメッセージを右上に表示
|
|
- 5 秒後の自動非表示と手動クローズに対応
|
|
|
|
### 未実装画面
|
|
|
|
- なし
|
|
|
|
## セキュリティ対応状況
|
|
|
|
### 対応済み
|
|
|
|
- パスワードは Argon2id でハッシュ化。
|
|
- セッションは HttpOnly Cookie。
|
|
- Cookie は `SameSite=Lax`。
|
|
- 本番環境では Secure Cookie を有効化する設定。
|
|
- 状態変更 API では CSRF トークンを必須化。
|
|
- 認証済み API はセッションからユーザーを取得。
|
|
- サイト API ではリクエスト上の `user_id` を信用しない。
|
|
- サイト設定 API でもサイト所有者をセッションユーザーで確認。
|
|
- サイト設定で選択された Webhook がログインユーザーのものか検証。
|
|
- アラート履歴 API はログインユーザーの履歴のみ返す。
|
|
- アラート既読更新 API はログインユーザーの履歴のみ更新する。
|
|
- アラート履歴削除 API はログインユーザーの履歴のみ削除する。
|
|
- アラート絞り込み条件は Zod で検証し、SQL はプレースホルダで組み立て。
|
|
- Webhook URL は HTTPS のみ許可。
|
|
- Webhook URL は `normalizeHttpsUrl` を通し、localhost / private IPv4 / loopback IPv4 を拒否。
|
|
- Webhook 更新・削除はログインユーザーの通知方法のみ対象。
|
|
- Push endpoint は HTTPS のみ許可。
|
|
- OpenSSL 呼び出しはタイムアウトを設定。
|
|
- 監視ジョブはサイト単位の失敗で全体を止めない。
|
|
- 外部通信を伴う監視処理は並列数を制限。
|
|
- API の未捕捉エラーと監視ジョブの開始・終了・失敗を構造化ログで出力。
|
|
- 証明書取得失敗も `certificate_check_failed` としてアラート化。
|
|
- Webhook / Push の送信失敗は `delivery_result` に記録。
|
|
- パスワード更新時は全セッションを削除し、現在の Cookie も削除。
|
|
- 2 段階認証解除には現在のパスワードと OTP を要求。
|
|
- アカウント削除には現在のパスワードを要求。
|
|
- アカウント削除は `users` の削除を起点に関連データを CASCADE で削除。
|
|
- ログイン時、TOTP が有効なユーザーは OTP 検証を必須化。
|
|
- 通知タイミングは 1 時間以上、17520 時間以内に制限。
|
|
- 同一サイト内の重複した通知タイミングを拒否。
|
|
- SQL はプレースホルダを使用。
|
|
- サイト URL は HTTPS のみ許可。
|
|
- `localhost`、private IPv4、loopback IPv4 を監視対象から拒否。
|
|
- サイト登録時は 3 秒以内に証明書期限を取得できる場合のみ登録する。
|
|
|
|
### 今後強化が必要
|
|
|
|
- IPv6 private / link-local / unique local address の追加検証。
|
|
- DNS 解決後の SSRF 対策。
|
|
- Webhook 送信先の詳細な SSRF 対策。
|
|
- ログイン試行回数制限。
|
|
- セッションローテーション。
|
|
- 2 段階認証。
|
|
- パスワード更新時の全セッション失効。
|
|
- アカウント削除時の完全削除確認。
|
|
|
|
## 検証結果
|
|
|
|
以下は成功済み。
|
|
|
|
```text
|
|
pnpm lint
|
|
pnpm test
|
|
pnpm exec vite build
|
|
```
|
|
|
|
API 動作確認:
|
|
|
|
- `GET /api/health` 成功。
|
|
- API 経由でユーザー登録成功。
|
|
- API 経由でサイト登録成功。
|
|
- API 経由でサイト設定保存成功。
|
|
- API 経由でサイト設定取得成功。
|
|
- API 経由でアラート履歴取得成功。
|
|
- API 経由でアラート既読更新成功。
|
|
- API 経由で Webhook 登録成功。
|
|
- API 経由で Webhook 更新成功。
|
|
- API 経由で Webhook 削除成功。
|
|
- API 経由で Push 購読情報登録成功。
|
|
- `pnpm monitor:once` 成功。
|
|
- API セキュリティ境界と証明書監視処理のテスト成功。
|
|
- サイト登録時の証明書期限初期取得と取得失敗時の登録拒否テスト成功。
|
|
- 権限付き実行で OpenSSL による実サイトの証明書期限取得成功。
|
|
- サンドボックス内の外部接続拒否時に証明書取得失敗アラート作成を確認。
|
|
- API 経由で表示名更新成功。
|
|
- API 経由で TOTP セットアップ・有効化成功。
|
|
- TOTP 有効ユーザーの OTP なしログインが 401 になることを確認。
|
|
- TOTP 有効ユーザーの OTP ありログイン成功。
|
|
- API 経由で TOTP 解除成功。
|
|
- API 経由でパスワード更新成功。
|
|
- パスワード更新後、旧セッションが 401 になることを確認。
|
|
- 新パスワードでログイン成功。
|
|
- API 経由でアカウント削除成功。
|
|
|
|
## 起動状況
|
|
|
|
実装時点で以下を起動確認済み。
|
|
|
|
```text
|
|
docker compose up -d postgres
|
|
pnpm dev
|
|
docker compose up -d --build
|
|
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
```
|
|
|
|
確認 URL:
|
|
|
|
```text
|
|
Frontend: http://127.0.0.1:5173/
|
|
API: http://127.0.0.1:3000
|
|
Docker App: http://127.0.0.1:3000/
|
|
```
|
|
|
|
## 既知の注意点
|
|
|
|
- `pnpm` の build script 承認で `esbuild` を承認済み。
|
|
- サンドボックス環境では Vite / Vitest の設定解決時に権限付き実行が必要だった。
|
|
- `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。
|
|
- Compose のアプリ公開ポートは `127.0.0.1` に限定し、外部 NIC へ公開しない。
|
|
- Compose の PostgreSQL はホストに公開せず、`app` / `monitor-worker` から内部ネットワーク経由でのみアクセスする。
|
|
- コンテナ内では `HOST=0.0.0.0` で待ち受け、ホスト側への公開範囲は Compose の `ports` で制御する。
|
|
- 実装確認用に API 経由でテストユーザーと `https://example.com/` / `https://example.org/` / `https://example.net/` のサイトを登録済み。
|
|
- 実装確認用にアラート履歴を手動挿入済み。
|
|
- 実装確認用に Webhook と Push 購読情報を API 経由で登録済み。
|
|
- サンドボックス内で `pnpm monitor:once` を実行すると、外部 TLS 接続が `Permission denied` になる場合がある。その場合は権限付き実行が必要。
|
|
- OpenSSL は PATH 上にない場合でも、Windows では Git 付属の `openssl.exe` を自動検出する。
|
|
- VAPID private key が未設定の場合、Push 通知送信は `delivery_result` に失敗として記録される。
|
|
- TOTP セットアップ画面は QR コードとシークレット文字列の両方を表示。
|
|
|
|
## 次に実装する候補
|
|
|
|
1. API 単体テスト / 統合テストの追加
|
|
2. ログイン試行回数制限
|
|
3. SSRF 対策の DNS 解決後チェック強化
|
|
4. E2E テストの追加
|