Files
Aislo/.agent/complete_and_old/auth_login_security_plan.md
T

19 KiB

로그인 & 보안 시스템 구현 계획서 (v1.0)

1. 프로젝트 개요

1.1 목표

  • B2B SaaS 환경에서 마스터-사용자 기반 조직 관리
  • 이메일 인증 기반 강화된 보안
  • 시스템관리자 전체 감시/관리 권한
  • 개인정보보호법 준수

1.2 적용 대상

  • 30인 미만 소규모 기업 (공용 이메일 허용)
  • 개인별 계정 운영 (마스터 승인제)
  • 한국 개인정보보호법 준수 필수

2. 계정 유형 및 역할 (3가지)

2.1 마스터 (Master / Company Admin)

정의: 회사 최초 가입자 (회사당 1명 권장, 복수 가능)

권한:

  • 회사 정보 입력/수정
  • 초대코드 발급 및 관리
  • 신규 팀원 가입 승인/거부
  • 팀원 목록 조회
  • 팀원 강제 제거 (내보내기)
  • 회사 구독/결제 관리
  • 회사 대시보드 (기본 통계)

DB 구분: users.is_master = true


2.2 팀원 (Member / Regular User)

정의: 마스터가 승인한 회사 소속 사용자

권한:

  • 서비스 기능 일반 이용 (B04~B09 워크플로우)
  • 개인 프로필 수정
  • 프로젝트 생성/수정/조회

DB 구분: users.is_master = false

상태 관리:

  • PENDING (승인 대기중)
  • ACTIVE (승인됨, 이용 가능)
  • INACTIVE (마스터가 내보냄, 접근 불가)
  • REJECTED (마스터가 거부함)

2.3 시스템관리자 (System Administrator)

정의: 개발사 (Aislo) 측 운영 담당자 = 당신

권한:

  • 모든 회사 데이터 조회 (슈퍼유저)
  • 회사/사용자 계정 생성/삭제/수정
  • 회사별 구독 상태 관리
  • 전체 시스템 Q&A/요청사항 처리 (회사 메일로 수신 → 검토/수정/삭제)
  • 관리자 대시보드
    • 활성 사용자 수
    • 하드웨어 리소스 모니터링 (CPU, 메모리, 디스크)
    • 소프트웨어 사용 현황 (업로드/다운로드/API 호출 통계)
    • 에러/경고 로그 모니터링
    • 회사별 파일 저장소 사용량
  • 마스터 계정 대체 관리 (마스터 부재 시)

DB 구분: users.role = 'SYSTEM_ADMIN' (별도 테이블로 관리)

접근: 별도 관리 포털 (프론트엔드에서 숨김)


3. 회원가입 및 조직 연결 흐름

3.1 마스터 가입 흐름

1단계: 회원가입 시작
   └─ 이메일 입력 (예: founder@gmail.com)
   └─ 비밀번호 입력

2단계: 이메일 인증
   └─ 시스템이 해당 이메일로 6자리 OTP 코드 발송
   └─ 사용자가 OTP 입력하여 인증

3단계: 회사 정보 입력
   └─ 회사명
   └─ 사업자번호 (선택)
   └─ 회사 전화번호
   └─ 주소
   └─ 대표자명

4단계: 가입 완료
   └─ DB에 Company 레코드 생성
   └─ 고유 초대코드 자동 생성 (예: ABC-789-XYZ)
   └─ users.is_master = true로 설정
   └─ 마스터 대시보드로 이동

3.2 팀원 가입 흐름 (요청 기반)

1단계: 팀원이 가입 신청
   └─ 이메일 입력 (예: member@gmail.com)
   └─ 비밀번호 입력

2단계: 이메일 인증
   └─ OTP 입력하여 인증

3단계: 회사 정보 입력 (선택지)
   ├─ [새 회사 등록하기] → 마스터 역할 시작
   └─ [기존 회사에 참여하기] → 4단계로 진행

4단계: 회사 선택/검색
   └─ "우리 회사가 이미 가입되어 있나요?"
   └─ 회사명으로 검색
   └─ 검색 결과에서 해당 회사 선택

5단계: 승인 요청
   └─ [가입 신청 보내기] 버튼 클릭
   └─ 백엔드: JoinRequest 레코드 생성
   └─ 백엔드: 마스터의 등록된 이메일로 승인 메일 발송
   
6단계: 마스터의 승인 메일 확인
   └─ 마스터가 받은 승인 메일:
      "홍길동(member@gmail.com)님이 우리 회사에 가입을 신청했습니다.
       [승인] [거부] 버튼"
   └─ 마스터가 [승인] 클릭
   └─ 팀원 users.status = 'ACTIVE'로 변경
   └─ 팀원에게 "승인되었습니다" 메일 발송

7단계: 팀원 로그인 가능
   └─ 팀원이 이제 서비스에 로그인 가능

💡 핵심 차이점

  • 초대코드 쪽지 방식 아님
  • 사용자가 스스로 요청 → 마스터가 메일로 승인

4. 이메일 인증 (MFA) 정책

4.1 로그인 프로세스

1단계: 아이디(이메일)/비밀번호 입력
2단계: 백엔드 검증 (계정 존재, 비밀번호 일치, 상태 = ACTIVE)
3단계: OTP 발송 및 입력
   └─ 등록된 이메일로 6자리 OTP 코드 발송
   └─ 사용자 입력 (유효시간: 5분)
4단계: 로그인 완료
   └─ JWT/세션 토큰 발급

4.2 이메일 인증 주기

상황 인증 여부
최초 가입 시 필수
로그인할 때마다 아니오 (UX 개선)
3개월 주기 재인증 필수
새로운 브라우저/기기 필수
비밀번호 변경 후 필수
관리자가 강제 로그아웃 필수 (재로그인 시)

구현 방식:

  • DB: users.last_email_verified_at 컬럼 추가
  • 로그인 시점에 "마지막 인증이 3개월 이상 지났는가?" 체크
  • 맞으면 이메일 인증 강제

5. 세션 관리 정책

5.1 세션 유지 시간

시나리오 유지 시간 이유
기본 세션 유지 12시간 대용량 파일 업로드 고려 (최대 20GB)
활동 중인 경우 연장 파일 전송/처리 중일 때는 타임아웃 제외 (마지막 요청으로부터 4시간)
로그인 유지 옵션 최대 30일 (선택사항) 차후 구현 검토

5.2 활동 감지 로직

활동으로 간주하는 API 엔드포인트:
- 파일 업로드/다운로드
- 프로젝트 저장
- 데이터 처리 요청
- 대시보드/페이지 조회

세션 연장 방식:
- 각 API 호출 시 last_activity_at 컬럼 업데이트
- 만료 판정: 
  if (current_time - last_activity_at > 4시간) 
    → 세션 자동 만료
  else if (current_time - created_at > 12시간)
    → 세션 자동 만료

5.3 세션 만료 처리

  • 프론트엔드: 만료 5분 전 알림 ("5분 후 로그아웃됩니다")
  • 백엔드: 만료 후 모든 요청 거부 (401 Unauthorized)
  • 재로그인 필요

6. 비밀번호 정책 (Google 기준 참고)

6.1 비밀번호 입력 요구사항

요구사항 기준
최소 길이 8자 이상
대문자 A-Z 최소 1개 (필수 아님, 권장)
소문자 a-z 최소 1개 (필수 아님, 권장)
숫자 0-9 최소 1개 (필수 아님, 권장)
특수문자 !@#$%^&* 등 (필수 아님, 권장)

정책: Google과 유사하게 "권장이지만 강제하지 않음"

  • 이유: 강제 정책은 사용자 이탈 증가

6.2 비밀번호 저장 (DB)

  • 평문 저장 절대 금지
  • bcrypt 또는 PBKDF2 해시 저장
  • Salt 포함 (bcrypt 내장)
  • 해시 라운드: 12라운드 이상

6.3 비밀번호 변경

  • 이전 비밀번호와 동일하면 거부
  • 변경 시 "모든 기기 로그아웃" 옵션 제공 (선택사항)
    • 마스터의 경우: 보안상 권장

7. 로그인 이력 및 감시 (Audit Log)

7.1 기록할 정보

로그인 성공:

  • user_id
  • email
  • login_timestamp
  • user_agent (브라우저명, OS 정보만 - 기기 ID 아님)
  • last_activity_at (업데이트 시작 시점)

로그인 실패:

  • email (계정 존재 여부 관계없이)
  • failed_timestamp
  • failure_reason (계정없음 / 비밀번호틀림 / 이메일인증실패 / 계정비활성 등)
  • user_agent

프로그램 사용 이력:

  • user_id
  • action_type (파일업로드 / 프로젝트저장 / 데이터처리 등)
  • action_timestamp
  • resource_id (프로젝트ID, 파일ID 등)
  • status (성공/실패)

7.2 IP 주소 수집 (법적 고려)

수집하지 않음 (권장)

  • 이유: 국내 개인정보보호법상 개인정보이며, 별도 동의 + 암호화 필수
  • 법적 리스크 회피

만약 수집한다면:

  • 회원가입 약관에 명시 ("보안 목적으로 로그인 IP 기록")
  • 개인정보 처리방침에 명시 (보관기간: 3개월)
  • DB에 암호화하여 저장 (평문 금지)
  • 마스터/시스템관리자만 조회 가능

현재 권장: IP 수집 제외, User-Agent(브라우저/OS)만 저장

7.3 데이터 보관 기간

로그 타입 보관 기간 자동 삭제
로그인 성공 1년 자동 삭제
로그인 실패 6개월 자동 삭제
프로그램 사용 2년 자동 삭제
삭제된 사용자 로그 법적 보유 기간 후 삭제 자동 삭제

8. 시스템관리자 대시보드

8.1 기능

📊 시스템 모니터링

  • 활성 사용자 수 (실시간)
  • 활성 회사 수
  • 일일/주간/월간 활동 통계 그래프

💾 리소스 모니터링

  • 하드웨어

    • CPU 사용률 (%)
    • 메모리 사용률 (%)
    • 디스크 사용량 (GB)
    • 활성 프로세스 수
  • 소프트웨어

    • 일일 API 호출 수
    • 업로드된 총 파일 크기 (GB)
    • 다운로드 횟수
    • 동시 접속자 수

👥 회사 관리

  • 회사 목록 조회 (가입일, 상태, 사용자 수)
  • 회사 구독 상태 확인
  • 마스터 정보 확인 및 변경
  • 회사 삭제 (강제)

👤 사용자 관리

  • 전체 사용자 목록
  • 사용자 상태 변경 (ACTIVE / INACTIVE / REJECTED)
  • 비밀번호 초기화 (관리자용)
  • 사용자 로그인 이력 조회

📧 Q&A / 요청사항 관리

  • 회사 메일로 수신된 Q&A/요청 목록
  • 상태 관리 (NEW / REVIEWED / RESOLVED / DELETED)
  • 요청 내용 검토 및 수정
  • 요청 삭제 및 히스토리 관리

⚠️ 에러/경고 로그

  • 시스템 에러 로그 조회
  • 비정상 로그인 시도 (같은 계정 5회 이상 실패)
  • 대역폭 초과 경고
  • 데이터베이스 에러

9. 보안 방어 로직

9.1 무차별 대입(Brute Force) 공격 방지

로그인 실패 5회 이상 → 계정 15분 잠금
- 로그인 실패 횟수 저장: login_failures (컬럼)
- 마지막 실패 시간: last_failed_at (컬럼)
- 15분 후 자동 해제

시스템관리자에게 알림 (캡파 or SMS)
- 5회 실패 시 시스템 관리자 메일 알림
- 10회 이상 실패 시 경고 레벨 상향

9.2 동시 로그인 처리 (개인별 계정 기준)

  • 동시 로그인 허용
    • 이유: 개인별 계정이므로 가정용 + 업무 기기 동시 사용 고려
    • 다만, 의심 로그인 시 알림 (예: 다른 국가/지역에서 동시 접속)

9.3 세션 토큰 보안 (확정: Secure HttpOnly 세션 쿠키)

선택: Secure HttpOnly 세션 쿠키 (JWT 아님)

  • 이유: 모놀리식 구조 + 중소규모 + 보안 최우선 + 세션 추적 필요

쿠키 설정:

  • 쿠키 이름: session_id
  • 유효 시간: 12시간
  • Secure 플래그: true (HTTPS만 전송)
  • HttpOnly 플래그: true (JS 접근 불가, XSS 방지)
  • SameSite: Lax (CSRF 방지)

세션 저장소: MariaDB sessions 테이블

CREATE TABLE sessions (
  id VARCHAR(255) PRIMARY KEY,
  user_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP NOT NULL,
  last_activity_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

10. 이메일 발송 인프라

10.1 발송해야 할 메일 유형

메일 타입 수신자 발송 타이밍 중요도
가입 확인 메일 신규 가입자 가입 완료 직후 🔴 필수
OTP 인증 코드 로그인/재인증 자 로그인 시도 직후 🔴 필수
팀원 승인 요청 마스터 팀원이 가입 신청 시 🔴 필수
가입 승인/거부 결과 팀원 마스터 승인/거부 직후 🟠 중요
3개월 재인증 알림 모든 사용자 3개월마다 정기적 🟠 중요
세션 만료 알림 활동 중인 사용자 만료 5분 전 🟡 알림
비밀번호 변경 알림 해당 사용자 변경 직후 🟡 알림
Q&A 답변 회신 문의자 시스템 관리자 처리 후 🟡 알림

10.2 메일 발송 서비스

권장 선택: AWS SES, SendGrid, 또는 한국 국내 서비스 (Solapi, Stibee)

이유:

  • 높은 배송 성공률 (99% 이상)
  • 빠른 발송 속도 (즉시~1초)
  • 스팸 필터 회피 (신뢰도 높음)
  • API 기반 통합 용이

구현 방식:

  • FastAPI 백엔드에서 비동기 작업 큐 (Celery + Redis)
  • 실패한 메일은 재시도 로직 (최대 3회)

11. DB 스키마 (최소 필수 테이블)

11.1 companies 테이블

CREATE TABLE companies (
  id INT AUTO_INCREMENT PRIMARY KEY,
  company_name VARCHAR(255) NOT NULL,
  company_code VARCHAR(20) UNIQUE NOT NULL,  -- 초대코드 역할
  business_number VARCHAR(20),
  phone_number VARCHAR(20),
  address VARCHAR(500),
  representative_name VARCHAR(100),
  master_user_id INT NOT NULL,
  status ENUM('ACTIVE', 'INACTIVE', 'SUSPENDED') DEFAULT 'ACTIVE',
  subscription_status ENUM('FREE', 'TRIAL', 'PAID', 'EXPIRED') DEFAULT 'FREE',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  FOREIGN KEY (master_user_id) REFERENCES users(id)
);

11.2 users 테이블

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  company_id INT,
  is_master BOOLEAN DEFAULT FALSE,
  status ENUM('PENDING', 'ACTIVE', 'INACTIVE', 'REJECTED') DEFAULT 'PENDING',
  last_email_verified_at TIMESTAMP,
  login_failures INT DEFAULT 0,  -- 실패 횟수
  last_failed_at TIMESTAMP,  -- 마지막 실패 시간
  account_locked_until TIMESTAMP,  -- 계정 잠금 해제 시간
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  FOREIGN KEY (company_id) REFERENCES companies(id)
);

11.3 join_requests 테이블

CREATE TABLE join_requests (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  company_id INT NOT NULL,
  status ENUM('PENDING', 'APPROVED', 'REJECTED') DEFAULT 'PENDING',
  requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  reviewed_at TIMESTAMP,
  reviewed_by INT,  -- 마스터 user_id
  review_comment TEXT,
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (company_id) REFERENCES companies(id),
  FOREIGN KEY (reviewed_by) REFERENCES users(id)
);

11.4 login_logs 테이블

CREATE TABLE login_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT,
  email VARCHAR(255),  -- 실패 시 user_id가 NULL일 수 있음
  status ENUM('SUCCESS', 'FAILURE') NOT NULL,
  failure_reason VARCHAR(100),
  user_agent TEXT,  -- 브라우저, OS 정보만
  login_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id),
  INDEX (user_id),
  INDEX (login_at)
);

11.5 activity_logs 테이블

CREATE TABLE activity_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  user_id INT NOT NULL,
  action_type ENUM('FILE_UPLOAD', 'FILE_DOWNLOAD', 'PROJECT_SAVE', 'DATA_PROCESS', 'PAGE_VIEW'),
  action_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  resource_id VARCHAR(255),  -- 프로젝트/파일 ID
  status ENUM('SUCCESS', 'FAILURE') DEFAULT 'SUCCESS',
  details JSON,  -- 추가 정보
  FOREIGN KEY (user_id) REFERENCES users(id),
  INDEX (user_id),
  INDEX (action_at)
);

11.6 system_admin_logs 테이블

CREATE TABLE system_admin_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  admin_user_id INT NOT NULL,
  action_type ENUM('USER_DELETE', 'COMPANY_DELETE', 'QA_MODIFY', 'QA_DELETE', 'RESET_PASSWORD'),
  target_id INT,  -- 대상 사용자/회사 ID
  action_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  details JSON,
  FOREIGN KEY (admin_user_id) REFERENCES users(id),
  INDEX (admin_user_id),
  INDEX (action_at)
);

11.7 support_requests 테이블 (Q&A)

CREATE TABLE support_requests (
  id INT AUTO_INCREMENT PRIMARY KEY,
  company_id INT,
  user_email VARCHAR(255),
  subject VARCHAR(255) NOT NULL,
  message TEXT NOT NULL,
  status ENUM('NEW', 'REVIEWED', 'RESOLVED', 'DELETED') DEFAULT 'NEW',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  reviewed_at TIMESTAMP,
  reviewed_by INT,  -- 시스템 관리자 user_id
  review_notes TEXT,
  FOREIGN KEY (company_id) REFERENCES companies(id),
  FOREIGN KEY (reviewed_by) REFERENCES users(id),
  INDEX (status),
  INDEX (created_at)
);

12. 구현 우선순위 (Phase)

Phase 1 (필수): 기본 인증 및 회원가입

  • 회원가입 로직 (이메일 OTP 포함)
  • 마스터 회사 정보 입력
  • 로그인 로직 (비밀번호 + 이메일 OTP)
  • 세션/토큰 관리
  • 사용자 상태 관리 (ACTIVE/INACTIVE)

Phase 2 (필수): 팀원 가입 승인제

  • 팀원 가입 신청 기능
  • 가입 요청 알림 메일 (마스터 수신)
  • 마스터 승인/거부 대시보드
  • 승인/거부 결과 메일 (팀원 수신)

Phase 3 (중요): 보안 강화

  • 3개월 주기 재인증 로직
  • 로그인 이력 기록 (login_logs)
  • 무차별 대입 공격 방지 (5회 실패 잠금)
  • 활동 로그 기록 (activity_logs)

Phase 4 (중요): 마스터 관리 기능

  • 팀원 목록 조회
  • 팀원 강제 내보내기 (status = INACTIVE)
  • 마스터 대시보드 (회사 통계)

Phase 5 (고급): 시스템관리자 포털

  • 관리자 대시보드
  • 회사/사용자 관리
  • Q&A 관리
  • 리소스 모니터링
  • 에러 로그 모니터링

13. 주요 검토 사항

13.1 이메일 발송 인프라

  • SMTP 서버 선정 (AWS SES / SendGrid / Solapi)
  • 테스트: 가입 메일 발송 성공률 확인
  • 스팸 필터 회피 전략 (SPF, DKIM, DMARC 설정)

13.2 비밀번호 해싱 라이브러리

  • Python bcrypt 또는 argon2 설정
  • 라운드 수 결정 (권장: 12 이상)

13.3 JWT vs 세션 쿠키

  • 선택: Secure HttpOnly 쿠키 (권장, CSRF/XSS 방어)
  • 토큰 만료 시간 설정

13.4 개인정보 처리방침 작성

  • 로그인 이력 수집 목적 명시
  • 보관 기간 명시
  • 사용자 동의 형식 결정

14. 다음 단계

  1. 이 계획서 검토 (당신의 피드백)
  2. 📝 세부 기술 사양 작성 (.agent/backend.md 업데이트)
  3. 💻 Phase 1 코드 구현 시작 (회원가입 + 로그인)
  4. 🧪 단위 테스트 작성
  5. 🎨 프론트엔드 UI/UX 설계 (.agent/frontend.md)
  6. 📊 통합 테스트 및 검증

작성일: 2026-07-07
담당자: 시스템 설계 팀
상태: 초안 (v1.0)