왜 OAuth 2.0인가
"비밀번호를 직접 넘기지 않고도 제3자 앱에 권한을 부여할 수 있을까?" 2012년 IETF(Internet Engineering Task Force)가 RFC 6749로 발표한 OAuth 2.0은 바로 이 질문에 대한 답이었습니다. 발표 이후 10년이 넘는 시간 동안 Google, Facebook, GitHub, Kakao 등 거의 모든 주요 플랫폼이 OAuth 2.0을 표준 인증 프로토콜로 채택했고, 2023년 기준 전 세계 웹 로그인의 약 68%가 OAuth 기반 소셜 로그인을 지원한다는 Okta의 조사 결과도 있습니다.
OAuth 2.0을 제대로 이해하려면 먼저 인증(Authentication)과 인가(Authorization)의 차이를 명확히 해야 합니다. 인증은 "당신이 누구인가?"를 확인하는 과정이고, 인가는 "당신이 무엇을 할 수 있는가?"를 결정하는 과정입니다. OAuth 2.0은 본래 인가 프레임워크지만, OpenID Connect(OIDC) 레이어를 얹으면 인증까지 처리할 수 있게 됩니다.
---
OAuth 2.0의 핵심 구성 요소
4개의 역할(Roles)
OAuth 2.0 프로토콜은 네 가지 역할을 정의합니다. 리소스 소유자(Resource Owner)는 보호된 자원에 대한 접근을 승인하는 주체로, 대부분의 경우 최종 사용자입니다. 클라이언트(Client)는 리소스 소유자의 승인을 받아 보호된 자원에 접근하는 애플리케이션을 말합니다. 인가 서버(Authorization Server)는 리소스 소유자를 인증하고 토큰을 발급하며, 리소스 서버(Resource Server)는 액세스 토큰을 검증하고 보호된 자원을 제공하는 역할을 합니다.
실무에서 인가 서버와 리소스 서버는 같은 시스템일 수도 있고, Google처럼 분리된 구조일 수도 있습니다. 중요한 것은 각 역할의 책임 범위를 명확히 이해하는 것이며, 이를 혼동하면 보안 설계에서 심각한 결함이 발생할 수 있습니다.
토큰의 종류와 역할
Access Token은 보호된 리소스에 접근하기 위한 자격 증명으로, 수명이 짧습니다(보통 15분~1시간). Refresh Token은 Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위한 장기 토큰입니다. ID Token은 OIDC에서 추가된 개념으로, 사용자 신원 정보를 담은 JWT 형식의 토큰이죠.
Access Token의 수명을 짧게 유지하는 이유는 토큰 탈취 시 피해 범위를 최소화하기 위함입니다. Refresh Token은 반드시 서버 측에 안전하게 저장해야 하며, 클라이언트 사이드에 노출되어서는 안 됩니다.
---
4가지 인증 흐름(Grant Types)
1. Authorization Code Grant (가장 안전)
웹 서버 애플리케이션에서 가장 널리 사용되는 방식으로, 보안성이 가장 높습니다. 사용자가 인가 서버에서 로그인하면 인가 코드(Authorization Code)가 클라이언트의 백엔드로 전달되고, 백엔드가 이 코드를 다시 인가 서버에 전송하여 토큰을 받는 2단계 과정을 거칩니다.
이 방식의 핵심 장점은 Access Token이 사용자의 브라우저를 거치지 않는다는 점입니다. 인가 코드는 일회성이며 짧은 유효기간(보통 10분)을 갖기 때문에, 중간자 공격에 대한 방어력이 뛰어납니다. 2023년 OAuth 2.1 초안에서는 이 방식을 PKCE와 함께 사용하는 것을 모든 클라이언트 유형에 대해 권장하고 있습니다.
2. Authorization Code + PKCE
PKCE(Proof Key for Code Exchange)는 원래 모바일 앱을 위해 설계되었지만, 현재는 SPA(Single Page Application)를 포함한 모든 퍼블릭 클라이언트에 권장되는 확장입니다. 클라이언트가 랜덤한 code_verifier를 생성하고, 이의 SHA-256 해시인 code_challenge를 인가 요청에 포함합니다. 토큰 교환 시 원본 code_verifier를 전송하면, 인가 서버가 해시를 비교하여 요청의 진위를 검증하는 구조입니다.
이를 통해 인가 코드가 탈취되더라도 code_verifier 없이는 토큰 교환이 불가능해지므로, 인가 코드 가로채기(Authorization Code Interception) 공격을 효과적으로 방어할 수 있습니다.
3. Client Credentials Grant
사용자 개입 없이 서버 대 서버(M2M, Machine-to-Machine) 통신에 사용됩니다. 클라이언트가 자신의 client_id와 client_secret을 인가 서버에 직접 제출하여 토큰을 받는 가장 단순한 흐름으로, 마이크로서비스 간 통신이나 배치 처리 시스템에서 활용됩니다.
4. Implicit Grant (사용 비권장)
토큰이 브라우저 URL Fragment로 직접 전달되어 보안 위험이 크기 때문에, OAuth 2.1에서는 공식적으로 폐기(deprecated)되었습니다. 기존에 이 방식을 사용하고 있다면 Authorization Code + PKCE로의 마이그레이션을 강력히 권장합니다.
---
실전 보안 체크리스트
Refresh Token Rotation
Refresh Token Rotation은 Refresh Token을 사용할 때마다 새로운 Refresh Token을 발급하고, 이전 토큰을 무효화하는 기법입니다. Auth0가 2020년에 이 방식을 기본 적용한 이후, 대부분의 주요 인증 서비스가 채택했습니다. 탈취된 Refresh Token이 사용되는 순간 모든 토큰 체인이 무효화되므로, 토큰 도난의 영향을 크게 줄일 수 있습니다.
State 파라미터와 CSRF 방어
인가 요청에 랜덤 state 값을 포함하고, 콜백에서 이를 검증하는 것은 CSRF(Cross-Site Request Forgery) 공격 방어의 기본입니다. state 값은 세션에 바인딩되어야 하며, 예측 불가능한 충분한 엔트로피(최소 128비트)를 가져야 합니다.
토큰 저장 전략
브라우저 환경에서 Access Token을 어디에 저장할지는 오랜 논쟁 주제입니다. localStorage는 XSS에 취약하고, Cookie는 CSRF에 취약합니다. 현재 가장 권장되는 패턴은 HttpOnly + Secure + SameSite=Strict 속성의 쿠키에 Refresh Token을 저장하고, Access Token은 메모리에만 보관하는 BFF(Backend-For-Frontend) 패턴입니다.
---
JWT(JSON Web Token)와 OAuth 2.0
JWT는 OAuth 2.0에서 토큰 형식으로 가장 많이 사용되는 표준(RFC 7519)입니다. Header, Payload, Signature 세 부분으로 구성되며, 서명을 통해 무결성을 보장합니다.
실무에서 주의해야 할 점이 있습니다. JWT는 자체 포함(self-contained) 토큰이므로, 발급 후 서버 측에서 강제 무효화가 어렵습니다. 이를 보완하기 위해 토큰 블랙리스트, 짧은 만료 시간, 또는 레퍼런스 토큰(Opaque Token)을 병행하는 전략이 필요합니다.
토큰 서명 알고리즘은 RS256(RSA + SHA-256) 또는 ES256(ECDSA + SHA-256)을 사용하세요. HS256(HMAC)은 대칭키 기반이므로 비밀키가 모든 검증 주체에 공유되어야 한다는 운영상의 제약이 있습니다.
---
실무 구현 시 흔한 실수 5가지
첫째, redirect_uri를 화이트리스트로 관리하지 않는 것입니다. 오픈 리다이렉터 취약점을 통해 인가 코드가 공격자에게 전달될 수 있으므로, 등록된 URI와 정확히 일치하는지 검증해야 합니다.
둘째, scope를 과도하게 요청하는 것입니다. 최소 권한 원칙(Principle of Least Privilege)에 따라 애플리케이션이 실제로 필요한 scope만 요청하세요. 사용자 신뢰도와 승인율에도 직접적인 영향을 미칩니다.
셋째, Access Token의 만료 시간을 너무 길게 설정하는 것입니다. 편의를 위해 24시간 이상으로 설정하는 경우가 있지만, 15분~1시간이 적절합니다. Refresh Token으로 갱신하는 패턴을 구현하는 것이 바른 접근입니다.
넷째, 에러 응답에서 민감한 정보를 노출하는 것도 흔한 실수입니다. "유효하지 않은 client_secret"이라는 메시지 대신 "인증에 실패했습니다"와 같은 일반적인 메시지를 반환하세요.
다섯째, HTTPS를 사용하지 않는 환경에서 OAuth를 구현하는 것은 모든 보안 대책을 무의미하게 만듭니다. 개발 환경이라도 TLS를 적용하는 습관을 들이는 것이 좋습니다.
---
OAuth 2.1과 미래
OAuth 2.1은 기존 2.0의 모범 사례(Best Practice)를 통합한 업데이트로, 주요 변경 사항은 다음과 같습니다. 모든 클라이언트에 PKCE가 필수화되고, Implicit Grant와 ROPC(Resource Owner Password Credentials) Grant가 폐기됩니다. Refresh Token은 발신자 제한(sender-constrained) 또는 일회성(one-time-use)이 권장되며, 리다이렉트 URI의 정확한 문자열 매칭이 요구됩니다.
OAuth 2.0은 단순한 라이브러리 호출이 아니라 보안 아키텍처 설계입니다. 각 Grant Type의 보안 특성을 이해하고, 애플리케이션의 유형과 위협 모델에 맞는 흐름을 선택하세요. 편의를 위해 보안을 타협하는 순간, 사용자의 데이터가 위험에 노출됩니다.