사용자가 신뢰할 수 있는 인증

DontCode 위에서 앱을 만들면, 사용자의 자격 증명은 우리 스스로에게 적용하는 것과 같은 보안 기준으로 보호됩니다. 우리가 정확히 무엇을 하고 있는지, 그리고 여러분이 신경 쓰지 않아도 되는 것이 무엇인지 정리했습니다.

6분 소요
보안

이 페이지가 존재하는 이유

DontCode로 앱을 출시한 뒤 누군가 "비밀번호는 어떻게 처리하나요?"라고 묻는다면, 이 페이지를 보여주는 것만으로 답변이 끝나야 합니다. 인증은 잘못된 기본값 하나가 조용히 미래의 사고로 이어지는 영역입니다. 그래서 올바른 기본값을 우리가 대신 선택해 둡니다. 이 페이지에서는 비밀번호가 어떻게 저장되는지, brute-force 시도가 어떻게 차단되는지, 2차 인증이 어떻게 동작하는지, 그리고 유출된 세션이 어떻게 즉시 끊기는지 모두 다룹니다.

내부 보안 점검 (Internal review)
점검일: 2026-05-29

왜 우리 스스로를 점검하는가

고객 앱의 보안은 100% 방어적이어야 한다는 전제에서 출발합니다. 그래서 자체 점검 기준(rubric)을 만들고, 우리의 인증 시스템을 정기적으로 그 기준에 비추어 점검합니다. 이 페이지는 항목별 현재 결과를 모두 보여줍니다. 적합한 외부 감사 파트너를 찾는 대로 외부 점검도 진행할 계획입니다.

점검 기준

각 카테고리는 4개의 고정된 이진(binary) 기준으로 평가됩니다. 모든 기준은 PASS 또는 FAIL뿐이며 부분 점수는 없습니다. 모든 카테고리에 동일한 기준이 적용되므로 주관적인 가중치 부여가 없습니다.

C1

문서화

C2

구현 적정성

C3

경계 케이스

C4

감사 가능성

카테고리별 결과

각 셀에 마우스를 올리면 판정 근거가 표시됩니다.
비밀번호 해싱
솔트와 타이밍 안전성
격리와 데이터 범위
전송 구간 보안
자격 증명 위생
Brute-force 차단 및 레이트 리밋
MFA와 세션 관리

현재 상태

7개 카테고리 중 6개가 모든 기준을 통과했습니다. 남은 2개 항목은 아래에 정리되어 있으며, 다음 사이클에 처리하기로 예정되어 있습니다.

예정된 조치 항목

  • IP 단위 / 계정 간 레이트 리밋

    Brute-force 차단 및 레이트 리밋 · C4 · 감사 가능성

    기존 이메일 단위 한도에 더해 IP 단위 레이트 리밋 계층을 추가하여, 다수의 IP와 다수의 계정에 분산되는 자격 증명 스터핑이 통제 아래로 들어오도록 합니다.

  • TOTP 저장 시 암호화 세부 사항

    MFA와 세션 관리 · C4 · 감사 가능성

    TOTP 비밀에 대해 pgcrypto가 사용하는 정확한 알고리즘, 키 길이, 키 보관 위치를 확정해 공개함으로써, 저장 시 암호화의 강도를 외부에서 직접 검증할 수 있게 합니다.

이 점검은 자체 감사 기준을 사용한 내부 점검입니다. 외부 기관의 인증이 아닙니다. 적합한 파트너를 찾는 대로 외부 점검도 진행할 계획입니다.

비밀번호는 Argon2id로 해시됩니다

사용자의 비밀번호 자체는 절대 저장하지 않습니다. 모든 비밀번호는 Argon2id를 거쳐 처리됩니다. Argon2id는 국제 비밀번호 해싱 경진대회(Password Hashing Competition) 우승 알고리즘이자, OWASP가 현재 권장하는 표준입니다. 결과로 나온 지문(fingerprint)만 저장됩니다.

Argon2id는 설계상 메모리 의존적(memory-hard)입니다. 즉, MD5, SHA-1, 심지어 bcrypt 같은 구형 방식을 위협해 온 GPU/ASIC 기반 크래킹 공격에 강합니다. 작업 비용 파라미터를 명시적으로 고정해 두었기 때문에, 라이브러리 업데이트로 인해 비용이 조용히 약해질 일이 없습니다.

현재 파라미터

알고리즘
argon2id
메모리 비용
65,536 KiB (64 MiB)
시간 비용
3 회 반복
병렬성
4 레인

비밀번호마다 고유한 솔트, 자동 적용

모든 비밀번호에는 해싱 전에 암호학적으로 무작위인 솔트(salt)가 부여됩니다. 두 사용자가 같은 비밀번호를 쓰더라도 저장되는 해시는 완전히 다릅니다. 레인보우 테이블이나 사전 계산 공격은 적용되지 않습니다.

상수 시간(constant-time) 검증

로그인 시 비밀번호 검증은 Argon2의 상수 시간 비교를 사용합니다. 저장된 해시를 한 바이트씩 추측할 수 있는 타이밍 사이드 채널 공격이 차단됩니다.

프로젝트마다 독립된 사용자 풀

모든 DontCode 앱은 자신만의 격리된 인증 풀을 갖습니다. 한 클라이언트의 사용자 자격 증명은 다른 클라이언트에서 접근할 수 없습니다. 데이터베이스 레벨에서도 모든 행이 풀 ID로 범위 지정되며, 모든 쿼리에서 그 경계가 강제됩니다.

Brute-force 차단과 계정 잠금

모든 로그인 시도는 이메일 단위로 두 가지 제한과 비교되어 카운트됩니다. 짧은 버스트 스로틀은 봇이 실시간으로 계정을 두드리는 것을 막습니다. 더 긴 잠금 윈도우는 단일 레이트 리밋을 우회하려는 느린 분산 brute-force 시도를 막습니다. 카운터는 로그인 성공 시, 그리고 비밀번호 재설정 시 초기화됩니다.

잠긴 계정에는 일반 로그인 실패와 구분되는 "계정 잠금(Account locked)" 응답이 반환됩니다. 정당한 사용자는 비밀번호 재설정으로 잠금을 해제할 수 있다는 점을 알게 되지만, 이메일을 무작위로 시도하는 공격자에게는 어떤 정보도 노출되지 않습니다.

현재 적용 한도

스로틀 윈도우
15분당 10회
잠금 윈도우
24시간당 25회
로그인 성공 시 초기화
비밀번호 재설정 시 초기화

다중 인증 (MFA / TOTP)

사용자는 인증 앱(Authy, 1Password, Google Authenticator 등 RFC 6238 TOTP 클라이언트)을 2차 인증 요소로 등록할 수 있습니다. 등록 시 8개의 일회용 복구 코드가 생성되어 안전하게 보관할 수 있습니다. 복구 코드는 저장 시 해시되며, 한 번 사용된 뒤에는 폐기됩니다.

MFA가 활성화된 사용자의 로그인은 바로 세션을 반환하지 않습니다. 비밀번호 검증을 통과하면 수명이 짧은(5분) 챌린지 토큰이 발급됩니다. 사용자는 TOTP 코드 또는 복구 코드를 챌린지 엔드포인트에 제출하여 로그인을 완료합니다. TOTP 비밀 자체는 pgcrypto로 저장 시 암호화됩니다. OAuth 로그인은 이 단계를 의도적으로 건너뜁니다. 외부 IdP가 이미 자체 MFA를 강제하기 때문입니다.

실질적으로 작동하는 세션 무효화

발급되는 모든 액세스 토큰에는 세션 에포크(session epoch), 즉 해당 사용자에게 고유한 단조 증가 카운터가 포함됩니다. 인증이 필요한 모든 요청에서 에포크가 여전히 유효한지 검증합니다. 토큰이 단순히 해독된다는 이유만으로 신뢰되지 않습니다.

사용자가 비밀번호를 재설정하면 에포크가 원자적으로(atomic) 증가합니다. 그 사용자에게 이전에 발급된 모든 토큰이 즉시 무효화되며, 자연 만료를 기다릴 필요가 없습니다. 자격 증명이 유출된 뒤 사용자가 비밀번호를 바꿔도 공격자가 작업을 계속할 수 있는 부류의 공격이 차단됩니다. 에포크 증가에 실패하면 비밀번호 재설정 자체가 실패합니다. fail-closed 정책입니다.

심층 방어 (Defense in depth)

좋은 해싱 알고리즘은 필요조건이지 충분조건이 아닙니다. 그 주변에서 우리가 추가로 무엇을 하는지 정리했습니다.

비밀번호는 로그에 남지 않습니다

비밀번호는 해싱하거나 검증하는 그 짧은 순간 동안만 메모리에 존재합니다. 로그, 에러 메시지, API 응답 어디에도 기록되지 않습니다.

로그인 실패는 일관된 메시지를 반환합니다

이메일이 없든 비밀번호가 틀렸든, 실패 시 동일한 에러를 반환합니다. 특정 이메일이 사용자 풀에 등록되어 있는지 여부를 외부에서 알아낼 수 없습니다.

재설정 토큰은 일회용이며 시간이 제한됩니다

비밀번호 재설정 토큰은 빠르게 만료되고, 한 번만 사용할 수 있으며, 검증된 이메일 전달과 결합되어 있습니다. 유출되거나 재사용된 토큰은 무의미합니다.

모든 통신은 TLS로 암호화됩니다

모든 인증 트래픽은 TLS로 암호화되어 전송됩니다. 암호화를 우회하는 경로나 "내부 전용" 엔드포인트는 존재하지 않습니다.

풀별 비밀번호 정책(선택 사항)

각 프로젝트는 비밀번호 정책을 독립적으로 강화할 수 있습니다. 최소 길이, 필수 문자 종류 등을 조정할 수 있으며, 기본값도 합리적으로 설정되어 있습니다. 더 엄격하게 가는 것은 토글 하나로 가능합니다.

작고 집중된 코드베이스

우리 인증 서비스는 의도적으로 작게 유지됩니다. 비밀번호 해싱, 레이트 리밋, MFA, 세션 무효화는 각각 lib/auth/ 디렉터리 아래 독립된 파일(credentials.ts, rate-limit.ts, mfa.ts, session-epoch.ts)에 모여 있어, 추론하기 쉽고, 내부 검토가 쉽고, 보안 팀이 살펴보기에도 단순합니다. 이 영역에서는 "영리함"보다 "지루할 정도로 정확함"을 택합니다.

여러분 쪽에서는 설정할 것이 없습니다

해싱 파라미터, 솔트 생성, 키 로테이션, 레이트 리밋 인프라, MFA 라이브러리, 세션 처리는 모두 우리가 책임집니다. 그리고 업계 가이드라인에 맞춰 지속적으로 갱신합니다. 여러분은 제품을 출시하시고, 사용자의 자격 증명은 우리가 안전하게 지키겠습니다.

  • 튜닝할 비밀번호 해싱 설정이 없습니다.
  • 관리할 솔트나 페퍼(pepper)가 없습니다.
  • 연결할 레이트 리밋 인프라가 없습니다.
  • 통합할 MFA 라이브러리가 없습니다.
  • 유지보수할 세션 무효화 로직이 없습니다.
  • 직접 로테이션해야 할 세션 시크릿이 없습니다.
  • "우리가 제대로 설정한 게 맞나?" 라는 질문 자체가 생기지 않습니다.

여기서 답하지 않은 질문이 있으신가요?

구체적인 사항을 묻는 보안 팀, 세부 내용이 필요한 컴플라이언스 감사, 구현 선택이 궁금한 누구든 환영합니다. 연락 주시면 안내해 드립니다.

CEO
HOUK ELIJAH STORM
사업자등록번호
802-87-03840
주소
서울특별시 강남구 논현로 10길 30, 505-제이39호 (개포동)
전화
010-9766-7338
이메일
storm@dontcode.co
    Dont Code