動作分析システム MFA導入手順書(Cognito + Angular + Django)

動作分析システム MFA導入手順書(Cognito + Angular + Django)

1. 目的

本手順書では、既存のRedmine認証を廃止し、
AWS Cognito を使用した多要素認証(MFA)へ移行する。

対象システム:

  • フロントエンド:Angular

  • API:Django REST Framework

  • DB:Amazon RDS MySQL

  • 実行基盤:EC2 + Docker

  • コンテナ管理:ECR

導入後の認証方式:

  • Cognito Hosted UI

  • MFA(TOTP)

  • JWT認証

  • Bearer Token認証


2. システム構成

@startuml

actor User

rectangle "Browser" {
  component "Angular" as FE
}

rectangle "AWS" {
  component "ALB" as ALB
  component "Cognito\nMFA(TOTP)" as Cognito

  rectangle "EC2" {
    component "Angular Container" as FEC
    component "Django API Container" as API
  }

  database "RDS MySQL" as RDS

  component "Secrets Manager" as Secrets
  component "SSM Parameter Store" as SSM
  component "CloudWatch Logs" as Logs
}

User --> FE
FE --> Cognito : Login
Cognito --> FE : JWT
FE --> API : Bearer JWT
API --> Cognito : JWT Verify
API --> RDS
API --> Secrets
API --> Logs

@enduml

3. セキュリティ設計

3.1 実施するセキュリティ対策

項目 内容
MFA TOTPを必須化
HTTPS ALBでTLS終端
JWT検証 issuer / audience / exp検証
DB保護 RDSをPrivate Subnet配置
SSH禁止 Session Manager利用
秘密情報 Secrets Manager利用
監査ログ CloudWatch / CloudTrail
Docker ECR Image Scan有効化

4. AWS事前準備

4.1 Secrets Manager作成

保存する情報:

{
  "DJANGO_SECRET_KEY": "xxxxxxxx",
  "DB_HOST": "xxxxx",
  "DB_NAME": "motion_analysis_db",
  "DB_USER": "admin",
  "DB_PASSWORD": "xxxxxxxx"
}

作成手順:

  1. AWS Consoleへログイン

  2. Secrets Manager

  3. 「新しいシークレットを保存」

  4. 「その他のシークレット」

  5. JSON入力

  6. 名前:

motion-analysis/prod/app-secrets

4.2 Cognito User Pool作成

作成手順

  1. Cognito

  2. 「User Pool作成」

  3. サインイン方式:

email
  1. MFA:

必須
  1. MFAタイプ:

Authenticator App
  1. App Client作成

許可するFlow:

Authorization Code Grant

PKCE:

有効

注意

以下は禁止:

Implicit Flow

理由:

トークン漏洩リスクが高いため

5. Angular実装

5.1 ライブラリ導入

npm install angular-oauth2-oidc

5.2 auth.config.ts

import { AuthConfig } from 'angular-oauth2-oidc';

export const authConfig: AuthConfig = {
  issuer:
    'https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_xxxxx',

  redirectUri: window.location.origin + '/auth/callback',

  clientId: 'xxxxxxxx',

  responseType: 'code',

  scope: 'openid email profile',

  strictDiscoveryDocumentValidation: false,
};

5.3 auth.service.ts

import { Injectable } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { authConfig } from './auth.config';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(private oauthService: OAuthService) {
    this.oauthService.configure(authConfig);
    this.oauthService.loadDiscoveryDocumentAndTryLogin();
  }

  login(): void {
    this.oauthService.initCodeFlow();
  }

  logout(): void {
    this.oauthService.logOut();
  }

  getToken(): string {
    return this.oauthService.getAccessToken();
  }
}

5.4 Token保存ルール

禁止:

localStorageへ長期保存

推奨:

sessionStorage

理由:

XSS被害時の漏洩リスク低減

6. Django実装

6.1 ライブラリ

pip install PyJWT cryptography requests djangorestframework boto3

6.2 環境変数

APP_SECRET_NAME=motion-analysis/prod/app-secrets
COGNITO_REGION=ap-northeast-1
COGNITO_USER_POOL_ID=xxxx
COGNITO_APP_CLIENT_ID=xxxx

6.3 Secrets取得

import json
import boto3
import os

def get_secret():
    client = boto3.client("secretsmanager")

    response = client.get_secret_value(
        SecretId=os.environ["APP_SECRET_NAME"]
    )

    return json.loads(response["SecretString"])

SECRET = get_secret()

6.4 JWT検証

payload = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],
    audience=settings.COGNITO_APP_CLIENT_ID,
    issuer=issuer,
)

必須検証:

項目 必須
signature ○
issuer ○
audience ○
exp ○
token_use ○

7. Dockerセキュリティ

7.1 Dockerfile

禁止:

FROM python:latest

推奨:

FROM python:3.12-slim

理由:

latestは予期しない更新が入るため

7.2 rootユーザー禁止

RUN useradd -m appuser

USER appuser

8. EC2セキュリティ

SSH禁止

SecurityGroup:

22番ポート閉鎖

接続方法:

SSM Session Manager

9. RDSセキュリティ

禁止:

Public Access = true

必須:

Private Subnet

10. CloudWatchログ

出力対象:

  • ログイン成功

  • ログイン失敗

  • MFA失敗

  • 権限拒否

  • JWT検証失敗


11. Redmine移行

移行方針

Redmine認証は段階的に停止

手順

Step1

Redmineユーザー一覧取得

Step2

Cognitoへユーザー登録

Step3

初回ログイン時にMFA登録

Step4

JWT認証へ切替

Step5

Redmine認証停止


12. 動作確認

確認項目

項目 OK条件
ログイン MFA成功
JWT APIアクセス成功
無効JWT 401
権限不足 403
DB接続 正常
CloudWatch ログ出力あり

13. 障害対応

Cognito障害

対応:

管理者緊急アカウントを用意

JWTエラー

確認:

issuer
audience
token_use

14. 本番リリースチェック

項目 状態
MFA必須化 □
HTTPS □
SSH閉鎖 □
Secrets Manager利用 □
JWT検証 □
CloudTrail有効 □
WAF有効 □
ECR Scan有効 □

15. やってはいけないこと

禁止事項:

Client SecretをGitへPush
JWTをlocalStorageへ保存
latestタグ利用
Public RDS
SSH開放
秘密情報をコード直書き