인증과 인가를 나누어 디버깅하기
인증은 사용자가 누구인지 확인하는 과정이고, 인가는 그 사용자가 무엇을 할 수 있는지 확인하는 과정이다. JWT, OAuth 2.0, OIDC, RBAC, AWS IAM까지 이 구분 위에서 동작한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
모든 서비스에는 로그인과 권한 판단이 함께 들어간다. 그래서 사용자 관련 이슈를 볼 때 가장 먼저 나누어야 할 질문은 이것이다. 지금 실패한 것은 인증인가, 아니면 인가인가. 인증, AuthN은 사용자가 누구인지 확인하는 과정이고, 인가, AuthZ는 확인된 사용자가 특정 행동을 할 수 있는지 판단하는 과정이다. 401은 보통 토큰이 없거나 만료된 인증 문제이고, 403은 토큰은 있지만 필요한 권한이 없는 인가 문제다.
- 02
인증은 신분증 검사에 가깝다. ID와 비밀번호, OAuth, SSO, API Key, JWT 토큰 같은 방식으로 이 사람이 누구인지 확인한다. 반대로 인가는 출입 권한 확인에 가깝다. 이미 홍길동이라는 사실은 확인했지만, 홍길동이 관리자 페이지에 들어갈 수 있는지는 별도의 판단이다. 역할 기반 RBAC, 정책 기반 PBAC, ACL 같은 모델은 모두 이 인가를 다루는 방식이다. 이 둘을 섞으면 디버깅 방향이 완전히 달라진다.
- 03
JWT는 서버가 매 요청마다 DB를 조회하지 않아도 토큰을 검증할 수 있게 해준다. 비유하면 위조 방지 스탬프가 찍힌 신분증이다. 토큰을 받은 서버는 발급 기관에 매번 물어보는 대신 서명을 검증해 진짜인지 확인한다. Access Token은 보통 짧은 유효기간을 가지며, 문서는 외부 API에서는 5분에서 15분, 내부 저위험 서비스에서는 최대 60분까지를 권장 범위로 제시한다. 만료되면 Refresh Token으로 새 토큰을 재발급받는다.
- 04
JWT에서 꼭 기억할 경계는 Payload가 암호화가 아니라는 점이다. Payload는 Base64로 인코딩된 값이라 누구나 디코딩할 수 있고, Signature는 위조 방지를 담당할 뿐 내용 자체를 숨기지는 않는다. 그래서 비밀번호나 주민등록번호 같은 민감 정보는 Payload에 넣으면 안 된다. 로그아웃이나 강제 만료가 필요하다면 각 토큰에 고유 ID인 jti를 부여하고, Redis 블랙리스트에 기록해 아직 만료되지 않은 토큰도 차단할 수 있다.
- 05
Refresh Token Rotation은 Refresh Token도 한 번만 쓰게 만드는 방식이다. 입장권을 사용할 때마다 새 입장권으로 교환해주는 구조라고 보면 된다. Rotation이 없으면 Refresh Token을 훔친 공격자는 만료 전까지 계속 새 Access Token을 받을 수 있다. Rotation이 있으면 탈취된 Refresh Token이 다시 사용되는 순간 서버가 재사용 시도를 감지하고 해당 세션의 모든 토큰을 무효화할 수 있다. 문서는 Refresh Token 수명을 7일에서 14일 정도로 제시한다.
- 06
JWT 보안에서 흔한 실수도 명확하다. alg: none 공격은 토큰 헤더의 alg 값을 신뢰해 서명 없는 토큰을 통과시키는 문제이므로, 허용 알고리즘을 화이트리스트로 지정해야 한다. iss, aud, exp 같은 Claim이 있어도 서버가 검증하지 않으면 다른 서비스가 발급한 토큰이 통과될 수 있다. secret, admin123, changeme 같은 약한 비밀 키도 위험하다. 문서는 최소 256비트, 즉 32자 이상의 랜덤 키를 사용하라고 정리한다.
- 07
분산 시스템에서는 JWT 알고리즘 선택도 성능보다 키 분배와 회전 모델이 더 중요하다. HS256은 하나의 비밀 키로 서명과 검증을 모두 처리하므로 단일 서버에는 단순하지만, 여러 서비스가 같은 비밀 키를 공유해야 한다. RS256은 개인 키로 서명하고 공개 키로 검증하므로 각 서비스가 공개 키만 가지고 토큰을 자체 검증할 수 있다. 모놀리스에서 API, Order, Payment, Notification 네 서비스로 분리되는 사례처럼 키를 여러 곳에 배포하고 동시에 회전해야 하는 순간, RS256과 JWKS 전환이 운영 부담을 줄인다.
- 08
OAuth 2.0은 비밀번호를 직접 넘기지 않고 제한된 리소스 접근 권한을 위임하는 인가 프로토콜이다. Authorization Code Flow에서 코드를 한 번 더 교환하는 이유는 코드가 URL에 노출되어도 짧은 시간 안에 만료되고 일회성이며, 실제 Access Token은 서버 사이 통신으로만 전달되게 하기 위해서다. OIDC는 OAuth 2.0 위에 인증 레이어를 추가한다. OAuth 2.0의 흐름을 쓰되 ID Token이라는 JWT를 추가로 받아 이 사람이 누구인지 확인한다.
- 09
RBAC는 역할별로 권한을 묶어 관리하는 가장 흔한 인가 방식이다. 핵심은 역할과 권한을 분리해, 역할은 넓은 접근 범주를 맡고 권한은 세부 동작을 제어하게 하는 것이다. 하지만 시간대, IP나 지역, 리소스 소유자와의 관계 같은 조건이 늘어나면 Admin-Asia-BusinessHours-OwnTeamOnly 같은 합성 역할이 폭발한다. 역할 수가 사용자 수에 비례해 자라기 시작하면 ABAC나 PBAC로 넘어갈 신호다.
- 10
처음 보는 인가 시스템도 Subject, Action, Resource 세 축으로 나누면 분석할 수 있다. 누가, 무엇을, 어디에 하는지 묻는 방식이다. 이 공식은 NestJS RBAC뿐 아니라 Kubernetes RBAC, PostgreSQL, AWS IAM, Linux capabilities에도 이어진다. AWS S3 Access Denied를 볼 때도 현재 Principal이 누구인지, 거부된 verb가 s3:GetObject인지 s3:ListBucket인지, 대상 Resource가 버킷인지 객체 ARN인지 구분해야 한다. 정리하면 401은 인증, 403은 인가이고, JWT, SSO, Refresh Token, AWS IAM은 모두 이 구분 위에서 이해해야 한다.
같은 레이어