비밀번호 재설정(Reset Password) 안전하게 만들기
토큰 발급/만료/1회성, 계정 유추 방지, 링크 보안까지 포함한 비밀번호 재설정 체크리스트
비밀번호 재설정은 “기능은 단순해 보이지만” 공격자 입장에선 계정 탈취의 중요한 통로입니다.
이 문서는 토큰 보안, 계정 유추 방지, 링크/리다이렉트 보안을 포함한 기본 체크리스트를 정리합니다.
관련 용어: Password reset token, Open Redirect, HttpOnly
1) 권장 플로우
POST /api/auth/forgot-password(email 입력)- (서버 내부) 토큰 생성 → DB 저장(해시) → 만료 시간 설정
- 이메일 발송(토큰 포함 링크)
POST /api/auth/reset-password(token + newPassword)- 토큰 1회성 처리(사용 즉시 폐기) + 기존 세션 무효화(선택)
2) 토큰은 “랜덤 + 짧은 TTL + 1회성”
토큰 생성(개념 예시)
import crypto from "crypto";
const token = crypto.randomBytes(32).toString("hex"); // 64 chars
DB 저장은 “해시”를 추천
토큰 원문을 DB에 저장하면 DB 유출 시 즉시 악용될 수 있습니다.
const tokenHash = crypto.createHash("sha256").update(token).digest("hex");
3) 계정 유추 방지(반드시)
forgot-password 요청에서 이메일이 존재하지 않아도:
- 응답은 항상 성공처럼 보이게(동일한 메시지/상태코드)
- 내부적으로만 “실제로 메일을 보냈는지” 기록
4) 링크 보안: 토큰이 새지 않게
- 링크는 반드시 HTTPS
Referrer-Policy를 적절히 설정해 토큰이 다른 사이트로 전달되지 않게- 토큰을 URL 쿼리로 쓸 경우, 프런트에서 즉시 토큰을 읽고 URL에서 제거(history replace)하는 방식도 고려
5) redirect 파라미터는 검증(오픈 리다이렉트 방지)
/auth?redirect=... 같은 UX는 편하지만, 검증이 없으면 피싱에 악용될 수 있습니다.
- same-origin URL만 허용
- 또는 “허용된 path prefix 목록”만 허용
관련 용어: Open Redirect
6) 운영 체크리스트
reset-password도 레이트리밋을 걸기(토큰 대입 공격)- 비밀번호 변경 후 기존 세션을 폐기할지 정책 결정(보안 vs UX)
- 재설정 메일 템플릿에 “요청하지 않았으면 무시” 문구 포함
같이 보면 좋은 문서:
- (라이브러리)
bcrypt - (라이브러리)
nodemailer 인증 API 레이트리밋 설계: 로그인/회원가입/비번재설정은 다르게