PLT-SEC-005 인증/인가 시스템 구현 가이드
이 구현 가이드는 DTx 플랫폼의 PLT-SEC-005 인증/인가 요구사항을 체계적으로 구현하기 위한 포괄적인 계획을 제시합니다.
📋 목차
1. 개요
1.1 목표
- 기존 JWT/토큰 인증 시스템을 강화하여 완전한 RBAC 구현
- MFA(Multi-Factor Authentication) 구현으로 보안 강화
- 포괄적인 감사 로깅 시스템 구축
- Redis 기반 권한 캐싱으로 성능 최적화
- 의료 데이터 보호 규정 준수 (GDPR, HIPAA)
1.2 규정 준수 요구사항
- DiGA: 독일 의료 규정에 따른 강화된 인증
- FDA: 미국 의료 기기 소프트웨어 보안 요구사항
- K-FDA: 한국 의료 기기 보안 표준
- GDPR: 개인정보 보호를 위한 접근 제어
- ISO 27001: 정보보안 관리 체계
1.3 성공 지표
- 인증 성공률: 99.9% 이상
- 평균 인증 응답 시간: 200ms 이하
- MFA 적용률: 관리자 100%, 일반 사용자 90% 이상
- 감사 로그 수집률: 100%
- 보안 인시던트: 0건 유지
2. 현재 상태 분석
2.1 기존 구현 현황 ✅
인증 시스템
// 이미 구현된 JWT 인증 시스템
libs/feature/auth/src/lib/guards/
├── jwt-auth.guard.ts // JWT 토큰 기반 사용자 인증
├── app-token.guard.ts // 앱 토큰 기반 앱 인증
└── validate-token.handler.ts // 토큰 검증 핸들러
복잡한 RBAC 시스템 (이미 구현됨)
- 15개 세분화된 역할:
patientUser,clinicianAdmin,systemAdmin등 - 도메인별 권한 체계: User, IAM, TimeMachine, Sleep 도메인
- Plan-Role 매핑: 8개 플랜 (
plan_therapeutic,plan_clinician등) - 다차원 권한 계산: Group 권한 ∩ Plan 권한
현재 역할 분류
// 환자 역할
roles/healthcare.patientUser // 전체 환자 기능
roles/healthcare.patientViewer // 읽기 전용 환자
// 의료진 역할
roles/healthcare.clinicianAdmin // 임상의 관리자
roles/healthcare.providerViewer // 의료 제공자 뷰어
// 임상시험 역할
roles/clinical.coordinatorUser // 임상시험 코디네이터
roles/clinical.monitorViewer // 임상시험 모니터
roles/clinical.researchAdmin // 연구 책임자
// 시스템 관리 역할
roles/admin.systemAdmin // 시스템 관리자
roles/data.managerUser // 데이터 관리자
roles/data.analystViewer // 데이터 분석가
roles/development.engineerViewer // 개발자
// 비즈니스 역할
roles/product.managerUser // 제품 관리자
roles/crm.managerAdmin // CRM 관리자
roles/regulatory.specialistUser // 규제 담당자
roles/support.agentUser // 고객 지원 담당자
도메인별 권한 체계 (이미 정의됨)
// User Domain
user.account.read, user.account.create, user.profile.update
// IAM Domain
iam.role.read, iam.role.create, iam.assignment.update
// Sleep Domain
sleep.log.create, sleep.goal.update, sleep.report.read
// TimeMachine Domain
timemachine.time.set, timemachine.config.update
2.2 강화가 필요한 부분 ⚠️
MFA 구현
- 다중 인증 시스템 미구현
- 관리자 계정 추가 보안 부족
- 보안 정책 자동화 부재
포괄적 감사 로깅
- 인증/인가 이벤트 상세 로깅 부족
- BigQuery 기반 로그 분석 미구현
- 실시간 보안 모니터링 부재
성능 최적화
- 권한 검증 캐싱 시스템 미구현
- Redis 기반 세션 관리 부족
- 응답 시간 최적화 필요
3. 아키텍처 설계
3.1 전체 시스템 아키텍처
3.2 인증 플로우
4. 구현 계획
4.1 1단계: 기존 시스템 강화 (예상 2주)
현재 권한 시스템 확장
// 기존 시스템에 추가될 권한 매트릭스
// libs/feature/auth/src/lib/types/enhanced-permission.types.ts
export interface EnhancedPermission {
id: string;
domain: string; // 기존 도메인 유지: user, iam, sleep, timemachine
resource: string; // 기존 리소스 확장
action: string; // 기존 액션 확장
conditions?: PermissionCondition[];
contextualScope?: ContextualScope;
}
export interface ContextualScope {
patientScope?: 'own' | 'assigned' | 'all';
organizationScope?: string[];
timeRestriction?: {
startTime?: string;
endTime?: string;
};
}
// 기존 15개 역할과 연동
export interface RolePermissionMapping {
roleId: string; // 예: roles/healthcare.patientUser
permissions: EnhancedPermission[];
planContext?: string; // 예: plan_therapeutic
groupContext?: string; // 예: patients.general
}
기존 RBAC 서비스 강화
// libs/feature/auth/src/lib/services/enhanced-rbac.service.ts
@Injectable()
export class EnhancedRbacService extends ExistingRbacService {
constructor(
private readonly existingPermissionService: PermissionService,
private readonly groupService: GroupService,
private readonly planService: PlanService,
private readonly cacheService: CacheService,
private readonly auditService: AuditService
) {
super();
}
// 기존 다차원 권한 계산 강화
async calculateEffectivePermissions(
userId: string,
context?: any
): Promise<EnhancedPermission[]> {
// 1. 기존 Group 기반 권한 계산
const groupPermissions = await this.getGroupBasedPermissions(userId);
// 2. 기존 Plan 기반 권한 계산
const planPermissions = await this.getPlanBasedPermissions(userId);
// 3. 교집합 계산 (기존 로직 유지)
const effectivePermissions = this.intersectPermissions(
groupPermissions,
planPermissions
);
// 4. 컨텍스트 기반 필터링 (신규 추가)
return this.applyContextualFiltering(effectivePermissions, context);
}
// 기존 시스템과 호환되는 권한 검증
async hasPermission(
userId: string,
domain: string,
resource: string,
action: string,
context?: any
): Promise<boolean> {
const cacheKey = `enhanced:permission:${userId}:${domain}.${resource}.${action}`;
// 캐시 확인
const cachedResult = await this.cacheService.get(cacheKey);
if (cachedResult !== null) {
return cachedResult;
}
// 효과적 권한 계산
const effectivePermissions = await this.calculateEffectivePermissions(userId, context);
// 권한 매칭
const hasPermission = effectivePermissions.some(permission =>
permission.domain === domain &&
permission.resource === resource &&
permission.action === action &&
this.evaluateContextualScope(permission, context)
);
// 결과 캐싱 (TTL: 5분)
await this.cacheService.set(cacheKey, hasPermission, 300);
// 감사 로깅
await this.auditService.logPermissionCheck({
userId,
permission: `${domain}.${resource}.${action}`,
granted: hasPermission,
timestamp: new Date(),
context
});
return hasPermission;
}
}
4.2 2단계: 기존 Guard 시스템에 MFA 통합 (예상 1주)
기존 JWT Guard 확장
// libs/feature/auth/src/lib/guards/enhanced-jwt-auth.guard.ts
@Injectable()
export class EnhancedJwtAuthGuard extends JwtAuthGuard {
constructor(
commandBus: CommandBus,
loggerService: LoggerService,
private readonly mfaService: MfaService,
private readonly enhancedRbacService: EnhancedRbacService
) {
super(commandBus, loggerService);
}
async canActivate(context: ExecutionContext): Promise<boolean> {
// 1. 기존 JWT 검증
const basicAuthResult = await super.canActivate(context);
if (!basicAuthResult) {
return false;
}
const request = context.switchToHttp().getRequest<ExtendedRequest>();
const user = request.user;
// 2. 사용자 역할에 따른 MFA 요구사항 확인
const userRoles = await this.enhancedRbacService.getUserRoles(user.id);
const requiresMfa = this.checkMfaRequirement(userRoles);
if (requiresMfa) {
// 3. MFA 검증 확인
const mfaToken = request.headers['x-mfa-token'];
if (!mfaToken) {
throw new UnauthorizedException('MFA token required');
}
const mfaValid = await this.mfaService.verifyMfaToken(user.id, mfaToken);
if (!mfaValid) {
throw new UnauthorizedException('Invalid MFA token');
}
}
return true;
}
private checkMfaRequirement(userRoles: string[]): boolean {
// 기존 역할에 따른 MFA 요구사항
const mfaRequiredRoles = [
'roles/admin.systemAdmin',
'roles/healthcare.clinicianAdmin',
'roles/data.managerUser',
'roles/regulatory.specialistUser'
];
return userRoles.some(role => mfaRequiredRoles.includes(role));
}
}
기존 시스템과 통합된 MFA 서비스
// libs/feature/auth/src/lib/services/integrated-mfa.service.ts
@Injectable()
export class IntegratedMfaService {
constructor(
private readonly existingUserService: UserService,
private readonly cacheService: CacheService,
private readonly auditService: AuditService
) {}
async setupMfaForRole(userId: string, roleId: string): Promise<MfaSetupResponse> {
// 기존 역할에 따른 MFA 방법 결정
const mfaMethod = this.getMfaMethodForRole(roleId);
switch (mfaMethod) {
case 'TOTP':
return this.setupTotpForUser(userId);
case 'EMAIL':
return this.setupEmailMfaForUser(userId);
default:
throw new BadRequestException(`No MFA required for role: ${roleId}`);
}
}
private getMfaMethodForRole(roleId: string): string | null {
const roleMfaMapping = {
'roles/admin.systemAdmin': 'TOTP',
'roles/healthcare.clinicianAdmin': 'TOTP',
'roles/data.managerUser': 'EMAIL',
'roles/regulatory.specialistUser': 'EMAIL'
};
return roleMfaMapping[roleId] || null;
}
}
4.3 3단계: 기존 Guard 시스템 확장 (예상 1주)
기존 Permission 검증 강화
// libs/feature/auth/src/lib/guards/enhanced-permission.guard.ts
@Injectable()
export class EnhancedPermissionGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private readonly enhancedRbacService: EnhancedRbacService,
private readonly auditService: AuditService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
// 기존 방식으로 권한 요구사항 추출
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
'permissions', [context.getHandler(), context.getClass()]
);
if (!requiredPermissions || requiredPermissions.length === 0) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
if (!user) {
throw new UnauthorizedException('User not authenticated');
}
// 기존 도메인.리소스.액션 형식으로 권한 확인
for (const permission of requiredPermissions) {
const [domain, resource, action] = permission.split('.');
const hasPermission = await this.enhancedRbacService.hasPermission(
user.id,
domain,
resource,
action,
this.buildContextFromRequest(request)
);
if (!hasPermission) {
// 감사 로깅
await this.auditService.logPermissionDenied({
userId: user.id,
permission,
endpoint: `${request.method} ${request.path}`,
timestamp: new Date()
});
throw new ForbiddenException(`Missing permission: ${permission}`);
}
}
return true;
}
// 기존 컨트롤러와 호환되는 데코레이터
@SetMetadata('permissions', ['user.profile.read'])
static RequirePermission(...permissions: string[]) {
return SetMetadata('permissions', permissions);
}
}
// 기존 컨트롤러에서 사용 예시
@Controller('users')
export class UserController {
@Get('profile')
@UseGuards(EnhancedPermissionGuard)
@EnhancedPermissionGuard.RequirePermission('user.profile.read')
async getUserProfile(@Req() request) {
// 기존 로직 유지
}
@Post('sleep-log')
@UseGuards(EnhancedPermissionGuard)
@EnhancedPermissionGuard.RequirePermission('sleep.log.create')
async createSleepLog(@Req() request) {
// 기존 로직 유지
}
}
5. RBAC 시스템 구현
5.1 권한 매트릭스
의료 데이터 접근 권한
| 역할 | 환자 데이터 조회 | 환자 데이터 수정 | 의료진 노트 | 시스템 설정 | 감사 로그 |
|---|---|---|---|---|---|
| Patient | Own Only | Own Only | Read Only | None | None |
| Clinician | Assigned Only | Assigned Only | Full | None | Read Only |
| Admin | All | All | Full | Full | Full |
| System | All | All | None | Read Only | Write Only |
5.2 동적 권한 조건
소유권 기반 접근 제어
export const USER_OWNERSHIP_CONDITION: PermissionCondition = {
field: 'userId',
operator: 'eq',
value: '{{current_user_id}}'
};
export const ASSIGNED_PATIENT_CONDITION: PermissionCondition = {
field: 'assignedClinician',
operator: 'eq',
value: '{{current_user_id}}'
};
6. MFA 구현
6.1 지원 MFA 방법
TOTP (Time-based One-Time Password)
- Google Authenticator, Authy 지원
- 30초 윈도우, 6자리 코드
- QR 코드 기반 초기 설정
SMS 기반 MFA
- 국제 SMS 지원
- 5분 유효기간
- 일일 시도 횟수 제한 (10회)
이메일 기반 MFA
- 보안 이메일 발송
- 10분 유효기간
- HTML 템플릿 지원
6.2 MFA 정책
관리자 필수 MFA
export const MFA_POLICIES = {
admin: {
required: true,
methods: [MfaMethod.TOTP, MfaMethod.SMS],
timeout: 300 // 5분
},
clinician: {
required: true,
methods: [MfaMethod.TOTP, MfaMethod.EMAIL],
timeout: 600 // 10분
},
patient: {
required: false,
methods: [MfaMethod.SMS, MfaMethod.EMAIL],
timeout: 900 // 15분
}
};
7. 감사 로깅
7.1 감사 이벤트 타입
인증 이벤트
export interface AuthenticationEvent {
eventType: 'authentication';
userId: string;
action: 'login' | 'logout' | 'token_refresh' | 'mfa_challenge';
success: boolean;
ipAddress: string;
userAgent: string;
timestamp: Date;
metadata?: any;
}
권한 이벤트
export interface AuthorizationEvent {
eventType: 'authorization';
userId: string;
resource: Resource;
action: Action;
granted: boolean;
context?: any;
timestamp: Date;
}
7.2 감사 로그 저장소
BigQuery 스키마
CREATE TABLE `audit_auth_events` (
event_id STRING NOT NULL,
event_type STRING NOT NULL,
user_id STRING,
action STRING NOT NULL,
resource STRING,
success BOOLEAN NOT NULL,
ip_address STRING,
user_agent STRING,
context JSON,
timestamp TIMESTAMP NOT NULL,
environment STRING NOT NULL
)
PARTITION BY DATE(timestamp)
CLUSTER BY user_id, event_type;
8. 모니터링 및 운영
8.1 주요 메트릭
인증 메트릭
- 로그인 성공/실패율
- MFA 성공/실패율
- 토큰 갱신 빈도
- 세션 지속 시간
권한 메트릭
- 권한 확인 응답 시간
- 권한 거부율
- 캐시 히트율
- 권한 체크 성공률
8.2 알림 정책
보안 알림
- 연속된 인증 실패 (5회 이상)
- 관리자 권한 사용
- 비정상적인 접근 패턴
- MFA 우회 시도
9. 배포 계획 (기존 시스템 강화 방식)
9.1 점진적 강화 전략
Phase 1: Dev 환경 - 기존 시스템 확장 (1주차)
- 기존 Guard 시스템에 Enhanced 버전 추가
- 현재 15개 역할과 호환되는 권한 캐싱 시스템 구현
- 기존 JWT 토큰 검증과 병행하는 감사 로깅 강화
- 롤백 준비: 기존 시스템은 그대로 유지
Phase 2: Stage 환경 - MFA 및 성능 최적화 (2주차)
- 기존 JWT Guard에 MFA 레이어 추가
- 현재 Role-Plan 매핑을 활용한 Redis 캐싱 구현
- 기존 도메인별 권한 체계와 연동된 성능 최적화
- 호환성 검증: 현재 API 엔드포인트 영향 없음 확인
Phase 3: Prod 환경 - 실운영 적용 (3주차)
- Feature Flag로 Enhanced Guard 점진적 활성화
- 기존 사용자 역할에 따른 자동 MFA 설정
- 실시간 권한 검증 모니터링
- 무중단 전환: 기존 시스템 대기 상태 유지
9.2 기존 시스템과의 호환성 보장
현재 시스템 보존
// 기존 Guard는 그대로 유지
@UseGuards(JwtAuthGuard) // 기존 방식 지원
@UseGuards(EnhancedJwtAuthGuard) // 신규 강화 방식
점진적 API 업그레이드
- Phase 1: 중요 API부터 Enhanced Guard 적용
- Phase 2: 일반 API로 확장
- Phase 3: 모든 API 적용 완료
- 검증 완료 후: 기존 Guard 단계적 제거
롤백 전략
- 기존 JWT 인증 시스템 완전 보존
- Feature Flag로 즉시 기존 시스템 복귀 가능
- 데이터베이스 스키마 변경 없음 (추가만)
9.3 성공 기준 (기존 시스템 기준)
기능적 요구사항
- ✅ 기존 API 호환성 100% 유지
- ✅ 권한 확인 응답 시간 증가 < 50ms
- ✅ 기존 15개 역할 완전 지원
- ✅ 현재 Plan-Role 매핑 유지
비기능적 요구사항
- ✅ 기존 시스템 가용성 유지 > 99.9%
- ✅ 사용자 경험 변화 없음
- ✅ 기존 토큰 정책 호환성 100%
📊 비용 최적화 (기존 인프라 강화 기준)
환경별 추가 비용 (월간)
Dev 환경 (기존 + 추가)
- BigQuery (감사 로그 강화): +$10/월
- Secret Manager (MFA 키): +$5/월
- Cloud Monitoring (추가 메트릭): +$10/월
- 기존 인프라 활용: Redis, JWT 시스템 등
- 총 추가 비용: +$25/월
Stage 환경 (기존 + 추가)
- BigQuery (감사 로그 강화): +$25/월
- Secret Manager (MFA 키): +$10/월
- Cloud Monitoring (추가 메트릭): +$20/월
- Redis 캐싱 강화: +$15/월
- 총 추가 비용: +$70/월
Prod 환경 (기존 + 추가)
- BigQuery (감사 로그 강화): +$150/월
- Secret Manager (MFA 키): +$25/월
- Cloud Monitoring (추가 메트릭): +$75/월
- Redis 고가용성 캐싱: +$50/월
- 고가용성 감사 시스템: +$100/월
- 총 추가 비용: +$400/월
💰 기존 대비 비용 효율성
- 기존 복잡한 RBAC 시스템 활용: 신규 개발 비용 $50,000 절약
- 현재 JWT 인프라 재사용: 월 $200 운영비 절약
- 점진적 강화 방식: 한 번에 모든 인프라 교체 불필요
🎯 결론 (수정된 현실적 접근법)
이 수정된 구현 가이드를 통해 PLT-SEC-005 인증/인가 요구사항을 기존 시스템을 활용하여 효율적으로 강화할 수 있습니다:
✅ 주요 성과
- 기존 복잡한 시스템 인정 - 이미 구현된 15개 역할과 도메인별 권한 체계 활용
- 점진적 강화 방식 - 새로운 시스템 구축 대신 기존 JWT/Guard 시스템 확장
- 무중단 마이그레이션 - Feature Flag 기반 단계적 적용으로 위험 최소화
- 비용 효율성 - 신규 개발 $50,000 절약, 월 운영비 대폭 절감
- 호환성 보장 - 현재 API 엔드포인트 및 사용자 경험 변화 없음
🔄 기존 시스템 대비 개선사항
| 구분 | 기존 시스템 | 강화된 시스템 |
|---|---|---|
| 인증 | JWT + AppToken | JWT + AppToken + MFA |
| 권한 관리 | 15개 정적 역할 | 15개 역할 + 동적 컨텍스트 |
| 캐싱 | 기본 | Redis 기반 성능 최적화 |
| 감사 로깅 | 제한적 | BigQuery 기반 포괄적 추적 |
| 모니터링 | 기본 | 실시간 보안 알림 시스템 |
📋 다음 단계
- ✅ 기존 시스템 영향도 분석 - 현재 15개 역할별 사용 현황 파악
- ✅ Phase 1 구현 - Dev 환경에서 Enhanced Guard 시스템 구축
- ✅ 호환성 테스트 - 기존 API 엔드포인트 정상 동작 확인
- ✅ 점진적 적용 - Stage → Prod 환경 순차 적용
- ✅ 성능 모니터링 - 기존 대비 응답 시간 및 가용성 추적
💡 핵심 인사이트
- "새로 만들기" 대신 "기존 강화": 이미 복잡하고 잘 설계된 시스템을 존중
- 위험 최소화: 기존 시스템 완전 보존하면서 점진적 개선
- 비용 효율성: 대규모 재개발 없이 보안 강화 목표 달성
- 실무 중심: 이론적 설계보다 실제 구현 가능한 계획 수립
이제 이 현실적인 구현 가이드를 바탕으로 기존 팀과 인프라를 활용하여 안전하고 효율적으로 PLT-SEC-005 요구사항을 충족할 수 있습니다.
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.1.0 | 2025-08-13 | bok@weltcorp.com | 최초 작성 |