"당신이 누구인가" 를 확인하는 과정(Authentication, AuthN).
인가와의 구분
-
인증(Authentication) : 너는 누구냐 — → 신분증 확인
-
인가(Authorization) — : 너는 뭘 할 수 있냐 → 출입증 권한 확인
-
반드시 인증이 먼저다. 누구인지 모르면 무엇을 허용할지 정할 수 없다.
HTTP 상태 코드가 이 구분을 반영한다 — 그런데 이름이 뒤바뀌어 있다.
| 코드 | 이름 | 실제 의미 |
|---|---|---|
| 401 | Unauthorized | 인증 실패 — 누구인지 모르겠다. 로그인해라 |
| 403 | Forbidden | 인가 실패 — 누군지는 알지만 권한이 없다 |
401의 이름이 "Unauthorized"인 것은 HTTP 명세의 역사적 실수다. 401은 인증, 403은 인가로 기억해야 한다.
인증 요소 세 가지
| 요소 | 뜻 | 예 |
|---|---|---|
| 지식 (something you know) | 아는 것 | 비밀번호, PIN |
| 소유 (something you have) | 가진 것 | 휴대폰, OTP 기기, 보안키 |
| 속성 (something you are) | 본인 특성 | 지문, 홍채, 얼굴 |
서로 다른 요소를 둘 이상 쓰는 것이 MFA(다요소 인증)다. 비밀번호 + PIN은 둘 다 "지식"이므로 MFA가 아니다.
비밀번호 저장 — 절대 원문으로 두지 않는다
- 평문 저장 — → 유출 시 즉시 전부 뚫림. 최악
- MD5/SHA-256 단순 해시 — → GPU로 초당 수십억 번 계산 가능. 사실상 뚫림
- SHA-256 + salt — → 레인보우 테이블은 막지만 무차별 대입에 약함
- bcrypt / scrypt / Argon2 — → ✅ 정답
왜 일반 해시로는 부족한가 — SHA-256은 빠르도록 설계됐다. 빠른 것이 여기서는 약점이다. 공격자도 빠르게 대입할 수 있기 때문이다.
bcrypt·Argon2는 일부러 느리게 설계됐고, 비용 계수를 올릴 수 있다.
- bcrypt cost=12 → 한 번 계산에 약 250ms
- 정상 로그인은 문제없지만, 초당 수십억 대입은 불가능해진다
- 하드웨어가 빨라지면 cost를 13, 14로 올리면 된다
솔트(salt) 는 사용자마다 다른 무작위 값을 섞는 것이다. 같은 비밀번호라도 해시가 달라져, 미리 계산해 둔 표(레인보우 테이블) 가 무력해진다. bcrypt는 솔트를 결과 문자열에 자동으로 포함한다.
인증 방식들
| 방식 | 상태 | 특징 |
|---|---|---|
| 세션-쿠키 | 서버가 보관 | 즉시 무효화 가능, 확장 시 공유 저장소 필요 |
| JWT | 클라이언트 보관 | 서버 조회 불필요, 무효화가 어렵다 |
| OAuth 2.0 | 위임 | 남의 서비스 자원 접근 권한을 받는 것 |
| OIDC | 위임 | OAuth 위에 신원 확인을 얹은 것 |
흔한 실수 — 사용자 존재 여부 노출
- **❌ "존재하지 않는 아이디입니다" — / "비밀번호가 틀렸습니다"**
- 어떤 아이디가 가입돼 있는지 알려 준다(계정 열거 공격)
✅ "아이디 또는 비밀번호가 올바르지 않습니다"
응답 시간도 맞춰야 한다. 없는 아이디는 해시 검증을 건너뛰어 빨리 응답하면, 시간 차이로 존재 여부가 드러난다.