OAuth2: 권한 위임과 인가 흐름의 이해 - Secure Coding 5-2
OAuth2가 권한 위임과 인가를 어떤 흐름으로 처리하는지, access token과 refresh token이 왜 필요한지, 구현 시 어떤 보안 위험을 봐야 하는지 정리합니다.
OAuth2: 권한 위임과 인가 흐름의 이해 - Secure Coding 5-2
OAuth2는 단순히 소셜 로그인을 붙이는 기술이 아니라, 사용자의 자원 접근 권한을 제3자 애플리케이션에 안전하게 위임하기 위한 인가 프레임워크다. 이 글은 구성 요소, Authorization Code Grant, 구현 예시, 그리고 실제 보안 위협까지 한 번에 정리한다.
What this post covers
- OAuth2의 핵심 개념과 구성 요소
- Authorization Code Grant 흐름과 토큰 구조
- 구현 시 자주 놓치는 취약점과 방어 포인트
OAuth2
📚OAuth2: 사용자 인증(Authentication)과 권한 부여(Authorization)를 분리하여, 제3자 애플리케이션이 사용자의 자원(Resource)에 안전하게 접근할 수 있도록 지원하는 프로토콜
- 복잡한 암호화나 서명 없이도 HTTPS 연결만으로 액세스 토큰을 주고받아 권한을 위임할 수 있다
가볍고, 애플리케이션 환경에 구애받지 않는 유연성을 확보, 구현 난이도를 크게 낮춤
- 예: 어떤 쇼핑몰에서 ‘구글 계정으로 로그인’ 버튼을 눌렀을 때 쇼핑몰 사이트가 여러분의 구글 비밀번호를 알고 있지 않지만, 구글 인증 서버가 사용자를 대신해 발급해 준 액세스 토큰만 전달받아, 이메일 주소나 프로필 정보를 가져오는 흐름
OAuth2 구성 요소
Client Application (클라이언트)
- 역할: 사용자를 대신해 리소스에 접근하려는 앱/웹사이트
- 식별정보:
Client ID(공개),Client Secret(비밀 키)
✅주요 동작:
- 권한 서버에 인증 요청
- Authorization Code 받음
- Access Token 요청 및 수신
- API 호출
Resource Owner (리소스 소유자)
- 역할: 실제 데이터의 주인 = 사용자(당신)
✅주요 동작:
- 권한 서버의 로그인 창에서 인증
클라이언트가 요청한 권한에 동의/거부
- 중요: 비밀번호를 클라이언트에 주지 않고, 권한 서버에만 입력
Authorization Server (권한 서버)
- 역할: 사용자 인증 + 토큰 발급
✅주요 동작:
- 사용자 로그인 확인
Authorization Code발급 (임시 코드)Access Token발급 (짧은 유효기간, 실제 API 호출용)Refresh Token발급 (긴 유효기간, 토큰 갱신용)- 검증: Client ID/Secret 확인, Scope 검증
Resource Server (리소스 서버)
- 역할: 실제 데이터를 저장하고 API 제공
✅주요 동작:
- Access Token이 유효한가?
- Token의 scope에 이 API 접근 권한이 있는가?
Token이 만료되지 않았는가?
- 동작: 검증 통과 시 → 데이터 반환
OAuth2 인증 흐름: Authorization Code Grant
- 사용자: 애플리케이션에 서비스 접속 시도
- 애플리케이션: 그 즉시 권한 서버의 인증 요청 페이지를 띄우도록 redirect
- 권한 서버: redirect된 로그인 페이지에서 정보 입력 요청
- 사용자: 로그인 정보 입력 및 권한 ‘허용’
- 권한 서버:
Authirization code, 즉 인가 코드를 발급하여 애플리케이션에 전달 - 애플리케이션: 받은 인가 코드 기반으로 리소스 서버 액세스 토큰(
Access Token) 요청을 권한 서버에 보낸다- 이때 Client Secret과 함께 인가 코드를 전송해서 자신을 인증
- 권한 서버: 이를 검증하고
Access Token을 발급 - 애플리케이션: 발급받은
Access Token으로 리소스 서버에 필요한 사용자 리소스 요청 - 리소스 서버: 방금 발급 받은
Access Token이 맞는지 권한서버와 크로스 체크 - 유효하면 OK 응답 받음
- 리소스 서버: 요청한 리소스 제공
- 애플리케이션: 서비스 이용 화면 렌더링 후 사용자에게 서비스 제공
인증 흐름의 시퀀스 다이어그램
OAuth2의 장단점
✅장점:
- 비밀번호 노출 위험 감소
- 사용자 비밀번호를 클라이언트에 전달 X(권한서버에서만 관리) → 보안성 향상
- SSO (Single Sign-On) 지원
- 한 번의 로그인으로 여러 App에 접근 가능 → 편의성 향상
- 토큰 기반 인증
- Access Token, Refresh Token을 활용하여 세션 관리 및 권한 검증에 효과적
- 유효기간을 짧게하여 토큰 탈취 피해 최소화
- 다양한 클라이언트 지원
- 여러 클라이언트에 유연하게 적용 가능, 확장성 향상
- 외부 인증 연동 용이
OAuth2의 단점
❌단점:
- 구현 복잡성
- 다양한 인증흐름과 토큰 관리가 복잡하여 잘못 구현하면 보안 취약점이 발생
- 클라이언트 비밀 관리 문제
- 표준 해석 차이
- OAuth2는 표준 자체가 유연하게 되어있어서 연동 시스템 간 구현 차이 발생
- 추가 보안 고려 필요
PKCE, 토큰 취소(Token Revocation), 타임스탬프/Nonce 등 추가 보안 대책이 없으면 리플레이 공격, 토큰 탈취 등의 위험 존재
- 서버 간 신뢰 관계 구성 필요
PKCE: 중간자 공격을 방어하는 방식 - 인가 코드 교환 보호Token Revocation: 필요할 때 이전에 발급된 토큰 즉시 무효화 가능타임스탬프/Nonce: 일회용 값 사용 - 과거 값 재사용 공격 무효화
OAuth와 OAuth2 비교
OAuth2 구현
1. Google Cloud 가입
https://cloud.google.com/apis?hl=ko
2. 새 프로젝트 생성
3. 서비스 동의 및 시작
4. 프로젝트 구성
- 앱 정보 > 앱 이름:
OAuth2 Test, 사용자 지원 이메일: 개인 메일 주소 - 대상 > 외부 선택
- 연락처 정보: 개인 메일 주소
- ‘만들기’ 버튼 선택
5. 클라이언트 만들기
6. 데이터 액세스 설정
7. 대상 설정
- 대상 > 테스트 사용자 > +
ADD USERS에 본인 메일 주소 입력
8. 설정 확인 및 테스트
- 아래 URL에 클라이언트 ID를 넣어 구글 로그인 창이 뜨는지 확인
https://accounts.google.com/o/oauth2/auth?client_id=클라이언트ID&redirect_uri=http://localhost:8000/login/oauth2/code/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile
9. Authorization Code 발급
- Google OAuth API 등록 필요
- 사용자가 구글 로그인을 마치고 나면
redirect_uri에 Authorization Code를 응답해 줌
10. LAB 환경에 소스 추가해서 OAuth2 연동 테스트
LAB 소스코드 application.properties 설정 추가
- 이제 LAB 코드를 실행시키고 앞서 시도해봤던 URL을 입력하여 구글 로그인 창이 나타나며, 계정을 선택하고 진행하면 STS 콘솔에 메시지가 제대로 출력되면 성공
Access Token 발급
기존 소셜로그인 메서드도 수정하고, getAccessToken 메서드도 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Authorization Code를 사용하여 Access Token 요청
//restTemplate.exchange()를 이용해 Google OAuth 서버와 통신
private String getAccessToken(String authorizationCode, String registrationId) {
String clientId = env.getProperty("oauth2." + registrationId + ".client-id");
String clientSecret = env.getProperty("oauth2." + registrationId + ".client-secret");
String redirectUri = env.getProperty("oauth2." + registrationId + ".redirect-uri");
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("code", authorizationCode);
params.add("client_id", clientId);
params.add("client_secret", clientSecret);
params.add("redirect_uri", redirectUri);
params.add("grant_type", "authorization_code");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity entity = new HttpEntity(params, headers);
ResponseEntity<String> response = restTemplate.exchange(tokenUri, HttpMethod.POST, entity, String.class);
return objectMapper.readTree(response.getBody()).get("access_token").asText();
}
- Authorization code와 함께 HTTP 요청으로 보내서 액세스 토큰을 받아오는 것을 확인
Resource Server에서 유저정보 받기
1
2
3
4
5
6
7
8
9
10
11
12
//Access Token을 Authorization: Bearer 헤더에 포함하여 Google API 호출.
//유저 ID, 이메일, 이름 등의 정보를 반환.
private JsonNode getUserResource(String accessToken, String registrationId) {
String resourceUri = env.getProperty("oauth2." + registrationId + ".resource-uri");
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
HttpEntity entity = new HttpEntity(headers);
return restTemplate.exchange(resourceUri, HttpMethod.GET, entity, JsonNode.class).getBody();
}
- 겟 유저 리소스라는 메서드를 추가
- 메서드 코드를 보면, HTTP 헤더에 베어러라는 키워드를 넣고, 액세스 토큰을 넣음
- 그 응답 값은 JSON 포맷으로 수신되므로, 아이디, 이메일, 닉네임 등을 적절히 추출하여 콘솔에 출력함
유저 정보 토대로 계정 연동
1
2
3
4
5
6
7
8
9
10
11
// 컨트롤러
MemberModel member = checkUserId(email);
if (member == null) {
throw new RuntimeException("User not found in local DB: " + email);
}
// 서비스
HttpSession session = request.getSession(true);
session.setAttribute("userId", member.getUserId());
session.setAttribute("userName", member.getUserName());
response.setHeader("Set-Cookie", "JSESSIONID=" + session.getId() + "; Path=/; HttpOnly; Secure");
로그인 성공
OAuth2 보안 위협 및 해결 방법
OAuth2 구현 시 발생 가능한 취약점
Redirect URI검증 미흡 - 중간에 공격자가 끼어듦- State 파라미터 미사용
- State 파라미터: CSRF 방어에 사용하는 랜덤 토큰
PKCE미적용PKCE(Proof Key for Code Exchange): 모바일 및 공용 클라이언트에서 Authorization 코드 탈취를 막기 위한 보안 기법
OAuth2 주요 취약점 사례
- Authorization Code 탈취
- 중간자 공격(MITM)이나 오픈 리다이렉트 취약점을 이용해, 인증 코드가 탈취되어 부적절한 토큰 발급 위험
- Access Token 유출
- HTTPS 미적용, 불안전한 토큰 저장(예: 로컬 스토리지, 브라우저 캐시 등)으로 토큰 노출 가능
- 토큰 재사용 및 리플레이 공격
- 만료되지 않은 토큰 또는 중복 요청 처리 미흡으로 인한 리플레이 공격, 부적절한 접근 권한 행사
- 클라이언트 시크릿 관리 취약점
- 클라이언트 시크릿(Client Secret)이 공개 클라이언트에 노출될 경우, 악의적 사용자가 토큰 발급 시도 가능
Access Token 유출 방지 전략
- 안전한 전송 및 저장
- HTTPS/TLS 적용
- 보안 쿠키 사용
- 토큰 관리 정책 강화
- 짧은 유효기간
- Refresh Token 도입
- 토큰 취소 및 재발급
- 애플리케이션 보안 강화
- XSS 방어: 입력값 검증, 인코딩 및 CSP(Content Security Policy) 적용
- PKCE 사용: 공개 클라이언트의 경우, PKCE 도입으로 Authorization Code 탈취 방지
Key takeaways
- OAuth2의 핵심은 인증과 권한 부여를 분리해서 안전한 자원 접근을 가능하게 만드는 것이다.
- Access Token, Refresh Token, Authorization Code의 역할을 구분해야 전체 흐름이 이해된다.
- 실제 구현에서는 Redirect URI, State, PKCE, 토큰 저장 방식이 보안 수준을 크게 좌우한다.


















