セキュリティ設計
本ドキュメントは、TASHIKAプラットフォームのセキュリティアーキテクチャ設計を定義します。
認証アーキテクチャ
トークンベース認証
BFF Proxy パターン を採用し、トークン管理を BFF サーバーサイドに完全移行する。ブラウザには HttpOnly Session Cookie のみを渡し、JWT Access Token / Refresh Token はブラウザに一切露出しない。
→ BFF Proxy パターンの詳細設計: BFF 認証アーキテクチャ
| トークン種別 | 有効期間 | 保存場所 | 用途 |
|---|---|---|---|
| Session Cookie | 7日 | ブラウザ(HttpOnly; Secure; SameSite=Strict) | BFF セッションの識別 |
| Access Token | 短命(15分) | BFF サーバーサイドセッション | Application 層への認証(ステートレス検証) |
| Refresh Token | 長命(7日) | BFF サーバーサイドセッション + サーバーDB | Access Tokenの再発行、セッション管理 |
- ブラウザに JWT が存在しないため、XSS による Access Token 窃取リスクを排除
- トークンリフレッシュは BFF 内部で自動実行され、ブラウザは関与しない
- セッション管理(同時セッション数制限、強制ログアウト等)は BFF セッション + Refresh Token のサーバーサイド追跡で実現
- SSO/OIDC(→ SR-012)の認証フローも BFF がリレーパーティとして処理し、IdP トークンはブラウザに露出しない(→ BFF 認証アーキテクチャ)
→ 満たす要件: SR-002 認証要件
多要素認証 (MFA)
- 管理者・代行事業者スタッフには TOTP による2要素認証を必須化
- Authenticator アプリ互換(Google Authenticator, Microsoft Authenticator等)
→ 満たす要件: SR-003 MFA要件
認可アーキテクチャ
システムロールと業務ロールの対応
業務ロールはワークフロー上の役割、システムロールは認可(何にアクセスできるか)を制御する。同一のシステムロールを持つユーザーが、異なる業務ロールを担う場合がある。
| 業務ロール | システムロール | 備考 |
|---|---|---|
| 申告者 | employee | — |
| 確認担当者 | tenant_admin / agency_staff | 組織モデルによる |
| 責任者 | tenant_admin / agency_admin | 組織モデルによる |
| 業務担当者 | tenant_admin | 代行モデルでも企業管理者が担う(連携結果の確認は確認担当者の業務範囲) |
| 代行管理者 | agency_admin | 代行モデルのみ |
| 企業管理者 | tenant_admin | — |
→ 業務ロールの定義: アクターとロール
ポリシーベース認可
単なるロールだけでなく、クレームに基づくポリシーを適用:
| ポリシー | 説明 |
|---|---|
| TenantPolicy | TenantId が一致しないデータへのアクセスをDBレベル(RLS)で遮断 |
| AgencyPolicy | 顧問先横断アクセスの制御 |
| ImpersonationPolicy | 代理ログイン時の個人情報マスキング、閲覧のみ許可 |
| ExternalApiPolicy | スコープに基づくAPIアクセス制御 |
Row Level Security (RLS)
- PostgreSQL RLS + アプリケーション側の Global Query Filter を併用
- 「アプリにバグがあっても他テナントデータは見えない」を構造的に保証
→ 満たす要件: SR-004 認可要件
セッション管理設計
| 項目 | 設定 |
|---|---|
| 同時セッション数 | 最大3デバイス(Refresh Tokenのサーバーサイド管理で制御) |
| アイドルタイムアウト | 30分 |
| 強制ログアウト | 管理者が実行可能(全デバイスのRefresh Tokenを即時無効化) |
| セッション一覧 | ユーザーが自分のアクティブセッションを確認・個別破棄可能 |
→ 満たす要件: SR-005 セキュリティポリシー要件
暗号化設計
データ暗号化
| 対象 | 方式 | 備考 |
|---|---|---|
| マイナンバー | AES-256 (アプリケーションレベル暗号化) | DBには暗号文のみ保存。下4桁のみ平文で保持(検索用) |
| 通信 | TLS 1.3 | HTTPS必須。TLS 1.2以下は拒否 |
| バックアップ | AES-256 | 本番とは異なる鍵で管理 |
鍵管理
HSM/KMS で鍵を保護し、アプリケーションコードから平文の鍵にアクセス不可とする。
| 鍵種別 | 用途 | 保護方式 | ローテーション | 方式 |
|---|---|---|---|---|
| KEK (Key Encryption Key) | DEK の暗号化 | KMS (HSM バック) | 年次自動 | KMS の自動ローテーション機能。旧バージョンは復号専用に保持 |
| DEK (Data Encryption Key) | マイナンバーの暗号化 | KEK で暗号化して DB 保存 | 年次 | デュアルキー方式。新 DEK で再暗号化バッチを非同期実行 |
| JWT 署名鍵 | Access Token の署名 | KMS (HSM バック) | 90日 | JWKS エンドポイントで公開鍵を配布。旧鍵は Access Token 有効期限(15分)+ バッファ後に無効化 |
| DB 接続 | PostgreSQL 認証 | Workload Identity 推奨 | 90日 | Workload Identity の場合はトークン自動更新。パスワード方式の場合はデュアルキー |
| TLS 証明書 | HTTPS 通信 | マネージド証明書 | 自動更新 | ACME プロトコルによる自動更新 |
鍵ローテーション(デュアルキー方式):
DEK および DB 接続パスワードのローテーションにはデュアルキー方式を採用する。
緊急ローテーション:
鍵の漏洩が疑われる場合は、通常のデュアルキー並行運用期間を省略し、即時無効化 → 新鍵生成 → 再暗号化を実施する(→ SR-011 シークレット管理)。
→ 満たす要件: CR-009 暗号鍵管理、SR-011 シークレット管理
API セキュリティ設計
通信セキュリティ
| 対策 | 設定 |
|---|---|
| HSTS | max-age=31536000; includeSubDomains |
| CSP | default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' |
| CORS | 許可オリジンをホワイトリストで管理 |
| レート制限 | テナントごと 1,000 req/min(トークンバケット方式) |
Webhook セキュリティ
- HMAC-SHA256 署名をヘッダーに付与し、受信側で検証
- ペイロードにマイナンバー等の機微情報を含めない
→ 満たす要件: SR-008 APIセキュリティ要件
ファイルセキュリティ設計
マルウェアスキャン
- アップロードされた全ファイル(画像/PDF/XML)に対し、保存前に自動ウイルススキャンを実行
- 感染ファイルの保存を防止
→ 満たす要件: SR-006 マルウェア対策要件
監査ログ設計
記録対象
- 「いつ、誰が、どの従業員のデータを見た/変更したか」の完全な操作ログ
- 代理ログイン時は「代行者」として明記
- マイナンバーの閲覧は特に厳格に記録(→ CR-011 マイナンバー法対応)
保存要件
- 7年間保持(申告データと同期)
- Write-Once ストレージで改ざん防止
テーブルスキーマ設計
DM-901 AuditLog の物理スキーマ:
| カラム | 型 | NULL | 説明 |
|---|---|---|---|
id | uuid (ULID) | NOT NULL | 主キー。時系列ソート可能な ULID |
tenant_id | uuid | NOT NULL | テナント ID(RLS フィルタ対象) |
actor_id | uuid | NOT NULL | 操作を実行したユーザー ID |
actor_role | text | NOT NULL | 操作時のロール(employee, tenant_admin, agency_staff 等) |
impersonated_by | uuid | NULL | 代理ログイン時の実行者 ID。直接操作時は NULL |
action | text | NOT NULL | 操作種別(view, create, update, submit, approve, decrypt_mynumber 等) |
resource_type | text | NOT NULL | 対象リソース種別(declaration, employee, withholding_slip 等) |
resource_id | uuid | NULL | 対象リソース ID。一覧表示等の場合は NULL |
tax_year | integer | NULL | 対象年度。年度に紐づかない操作の場合は NULL |
details | jsonb | NULL | 操作の詳細情報(変更前後の値等)。マイナンバー平文は絶対禁止 |
ip_address | inet | NOT NULL | クライアント IP アドレス |
user_agent | text | NULL | クライアントの User-Agent |
trace_id | text | NOT NULL | 分散トレーシング用のトレース ID |
created_at | timestamptz | NOT NULL | 記録日時(サーバー時刻) |
checksum | text | NOT NULL | ハッシュチェーン用の SHA-256 チェックサム |
改ざん防止設計(3層防御)
Layer 1: PostgreSQL Append-Only テーブル
audit_logsテーブルに UPDATE / DELETE を拒否するトリガーを設定- アプリケーション用 DB ロールには
INSERT権限のみを付与(UPDATE,DELETE,TRUNCATEは付与しない)
-- UPDATE/DELETE 拒否トリガー
CREATE OR REPLACE FUNCTION prevent_audit_log_modification()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'audit_logs table does not allow UPDATE or DELETE';
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER audit_log_immutable
BEFORE UPDATE OR DELETE ON audit_logs
FOR EACH ROW EXECUTE FUNCTION prevent_audit_log_modification();Layer 2: ハッシュチェーン
- 各行の
checksumは、前行のchecksum+ 当該行の全データ(checksumを除く)を連結した SHA-256 ハッシュ - テナントごとに独立したハッシュチェーンを構成
- 日次検証バッチがチェーンの整合性を検証し、不整合検出時は Sev2 アラートを発報
checksum[n] = SHA-256(checksum[n-1] || tenant_id || actor_id || action || ... || created_at)Layer 3: Cloud Storage Object Lock
- 日次エクスポートバッチが監査ログを Cloud Storage にエクスポート
- Retention Policy(7年)を設定し、物理的に削除不可とする
- エクスポートファイルのハッシュを別バケットに保存し、改ざん検知に使用
パーティショニング戦略
月次レンジパーティション(pg_partman で自動管理):
| ライフサイクル | 期間 | ストレージ | アクセスパターン |
|---|---|---|---|
| Hot | 0〜6ヶ月 | PostgreSQL(プライマリ) | リアルタイムクエリ、ダッシュボード表示 |
| Cool | 6ヶ月〜2年 | PostgreSQL(別テーブルスペース) | 管理者による検索、CSV エクスポート |
| Coldline | 2年〜7年 | Cloud Storage (Coldline) | 監査人による期間指定検索 |
| 削除 | 7年超 | — | pg_partman による自動ドロップ + Cloud Storage ライフサイクルルールで削除 |
検索 SLO
| 利用者 | ユースケース | 対象期間 | SLO |
|---|---|---|---|
| 企業管理者 | 操作履歴 CSV エクスポート | 直近1年 | ≤ 10秒 |
| sys_admin | リアルタイム操作監視 | 直近24時間 | ≤ 3秒 |
| 監査人 | 全期間のマイナンバーアクセス履歴 | 全期間(最大7年) | ≤ 1分 |
インデックス設計
| インデックス | カラム | 用途 |
|---|---|---|
ix_audit_logs_tenant_created | (tenant_id, created_at) | テナント別の時系列検索 |
ix_audit_logs_actor_created | (actor_id, created_at) | ユーザー別の操作履歴 |
ix_audit_logs_resource | (resource_type, resource_id, created_at) | リソース別の変更履歴 |
ix_audit_logs_mynumber_access | (action) WHERE action = 'decrypt_mynumber' | マイナンバーアクセス監査(部分インデックス) |
→ 満たす要件: CR-003 監査・統制、CR-010 監査ログ保持、DM-901 AuditLog