剣 KENSAI
← 블로그로 돌아가기
인증 보안 2026년 4월 3일 읽는 데 22분

OAuth 보안 테스트 체크리스트: 고위험 인증 버그 찾기

OAuth 2.0 취약점은 치명적 등급의 버그 바운티 보상 순위에서 꾸준히 상위권을 차지합니다. OAuth 구성 오류 하나로 발생한 계정 탈취가 조직 전체의 침해로 번질 수 있으며, 이러한 버그는 수십억 명이 사용하는 애플리케이션에서도 발견됩니다.

1만~10만 달러
OAuth 계정 탈취 보상금
치명적
영향 분류
42억 명 이상
전 세계 영향 사용자
OWASP A07
식별 및 인증 실패

보안 테스트를 위한 OAuth 2.0 기본 원리

ℹ️ OAuth 2.0 흐름

대상이 어떤 흐름을 사용하는지 알아야 테스트할 공격을 정할 수 있습니다. 인가 코드 흐름 (PKCE 적용)은 가장 안전하고 널리 쓰입니다. 현재는 폐기된 암시적 흐름 에는 많은 취약점이 있습니다. 클라이언트 자격 증명 (머신 간 통신)은 고유한 공격 표면을 지닙니다. 운영 환경에서 절대 사용하지 말아야 할 방식은 리소스 소유자 암호 자격 증명.

OAuth 핵심 구성요소


섹션 1: 인가 코드 가로채기

테스트 1.1 — redirect_uri의 오픈 리디렉션

인가 서버가 redirect_uri를 충분히 엄격하게 검증하지 않으면 공격자는 인가 코드를 자신이 통제하는 서버로 보낼 수 있습니다.

# 정상 요청
https://auth.example.com/oauth/authorize?
  client_id=app123&
  redirect_uri=https://app.com/callback&
  response_type=code&state=xyz

# 공격 시도
# 1. 경로 순회
redirect_uri=https://app.com/callback/../../../evil.com/steal

# 2. 추가 경로
redirect_uri=https://app.com.evil.com/callback

# 3. 프래그먼트 우회
redirect_uri=https://app.com/callback%23@evil.com

# 4. 쿼리 문자열 우회
redirect_uri=https://app.com/callback?next=https://evil.com

테스트 1.2 — state 매개변수 검증

⚠️ state 누락·취약 = OAuth의 CSRF

OAuth 흐름에서 CSRF 공격을 막는 것은 state 매개변수입니다. 이 값이 없거나 예측 가능하면 공격자는 피해자가 자신의 계정을 공격자 통제 신원에 강제로 연결하도록 만들 수 있으며, 이는 계정 탈취로 이어집니다.

# 다음 state 속성을 확인합니다.
# 1. 인가 요청에 존재하는가
# 2. 콜백에서 검증되는가
# 3. 암호학적으로 무작위인가(순차적이거나 예측 가능하지 않은가)
# 4. 일회용인가(재사용한 state가 거부되는가)

# 테스트: 같은 state 매개변수 재사용
curl -s "https://app.com/oauth/callback?code=VALID_CODE&state=PREVIOUSLY_USED_STATE"
# 예상 결과: 오류(state가 이미 사용됨)

테스트 1.3 — 코드 재사용

# 인가 코드는 일회용이어야 합니다
# 첫 교환 뒤 코드를 다시 전송해 테스트합니다.

# 첫 사용(정상)
POST /oauth/token
code=AUTH_CODE_123&grant_type=authorization_code&...

# 두 번째 사용(실패해야 함)
POST /oauth/token
code=AUTH_CODE_123&grant_type=authorization_code&...
# 예상 결과: {"error": "invalid_grant"}

섹션 2: PKCE 우회 테스트

테스트 2.1 — PKCE 강제 적용

PKCE는 인가 코드 가로채기 공격을 방지합니다. PKCE를 지원하는 서버가 이를 강제하지 않으면 공개 클라이언트가 취약해집니다.

# Start flow WITH PKCE (legitimate)
code_verifier = generate_random_string(64)
code_challenge = base64url(sha256(code_verifier))

GET /authorize?
  code_challenge=abc123&
  code_challenge_method=S256&...

# Attack: exchange code WITHOUT providing code_verifier
POST /token
grant_type=authorization_code&
code=INTERCEPTED_CODE&
redirect_uri=https://legitimate-app.com/callback
# Missing: code_verifier
# If this succeeds -> PKCE not enforced -> 치명적 취약점

테스트 2.2 — code_challenge_method 다운그레이드

# S256에서 보안성이 낮은 plain으로 다운그레이드를 시도합니다
GET /authorize?
  code_challenge=ACTUAL_CODE_VERIFIER&
  code_challenge_method=plain&...
  
# 허용된다면 서버가 S256 대신 plain을 사용할 수 있습니다
# 그런 다음 code_verifier = 원래 평문으로 교환합니다
POST /token
code_verifier=ORIGINAL_PLAIN_TEXT&...

섹션 3: 토큰 취약점

테스트 3.1 — Referrer를 통한 액세스 토큰 유출

⚠️ 프래그먼트와 쿼리의 토큰 처리 차이

암시적 흐름은 폐기됐지만 여전히 발견됩니다. 이 흐름에서 URL 프래그먼트(#access_token=...)의 액세스 토큰은 Referer 헤더로 전송되지 않지만 쿼리 매개변수(?access_token=...)의 토큰은 전송됩니다. 토큰이 URL에 어떤 방식으로 나타나는지 반드시 확인하십시오.

테스트 3.2 — 토큰 범위 권한 상승

# 최소 범위 요청
GET /authorize?scope=read:email&...

# 액세스 토큰을 받은 뒤 권한 없는 API 호출 테스트
GET /api/user/delete
Authorization: Bearer ACCESS_TOKEN_WITH_READ_SCOPE
# 200이 아니라 403을 반환해야 합니다

테스트 3.3 — JWT 액세스 토큰 취약점

# If access tokens are JWTs, test:

# 1. Algorithm confusion (RS256 -> HS256)
header = {"alg": "HS256", "typ": "JWT"}
# 공개 키를 HMAC 비밀 키로 사용해 서명

# 2. None 알고리즘
header = {"alg": "none", "typ": "JWT"}
# 일부 라이브러리는 서명 없는 토큰을 허용함

# 3. Kid 인젝션('kid'를 사용하는 경우)
header = {"alg": "HS256", "kid": "' OR 1=1--"}
# kid 매개변수의 SQL 인젝션

# 도구:
# jwt_tool: python3 jwt_tool.py TOKEN -T (변조)
# jwt-cracker: 취약한 비밀 키 검사

테스트 3.4 — 리프레시 토큰 순환 우회

# Test if old refresh tokens are invalidated after rotation
POST /token
grant_type=refresh_token&refresh_token=OLD_REFRESH_TOKEN

# If this returns a new token -> 이전 토큰이 무효화되지 않음
# 이전 리프레시 토큰을 훔친 공격자가 계속 새 액세스 토큰을 받을 수 있음

섹션 4: 계정 탈취 시나리오

테스트 4.1 — 이메일 확인 없는 OAuth 계정 연결

💡 고위험 발견 패턴

피해자가 이메일과 암호로 가입했고(이메일: victim@gmail.com), 공격자가 소유권을 Google에서 확인하지 않은 채 같은 이메일의 Google OAuth 계정을 연결해 피해자 계정을 탈취할 수 있다면 치명적 계정 탈취입니다. 여러 인증 제공자 연결을 허용하는 플랫폼에서 특히 흔합니다.

테스트 4.2 — 사전 계정 탈취

# 공격 시나리오:
# 1. 피해자가 가입하기 전에 공격자가 victim@example.com으로 계정을 생성
# 2. 공격자가 자신의 OAuth 제공자를 이 이메일에 연결
# 3. 피해자가 나중에 victim@example.com으로 Google OAuth 가입
# 4. 피해자가 공격자가 미리 만든 계정에 접근하거나 그 반대 상황 발생

# 테스트 방법:
# 1. 피해자 계정이 존재하기 전에 피해자 이메일로 가입
# 2. 다른 이메일의 OAuth 연결
# 3. 피해자의 향후 계정에 접근 시도

테스트 4.3 — 토큰 고정

# Some implementations allow specifying access_token in request
# or don't properly bind tokens to sessions

# Test: force a known token value
GET /oauth/callback?access_token=KNOWN_VALUE

# If the app accepts and uses this token -> 토큰 고정

테스트 4.4 — sub/이메일 클레임 혼동

# 애플리케이션이 sub가 아니라 이메일에 계정을 연결하는 경우:
# 악성 OAuth 제공자의 email 클레임에 피해자 이메일을 넣어 등록
# 일부 제공자는 임의의 이메일 클레임 설정을 허용함

# 사용자 지정 OAuth 제공자를 만들어 테스트:
{
  "sub": "attacker-unique-id",
  "email": "victim@example.com",
  "email_verified": true
}

섹션 5: 인가 서버 취약점

테스트 5.1 — 클라이언트 인증 취약점

# Confidential clients must authenticate with client_secret
# Test if client_secret is optional:
POST /token
grant_type=authorization_code&
code=CODE&
client_id=CONFIDENTIAL_CLIENT_ID&
# Missing: client_secret
# If this works -> 클라이언트 인증이 강제되지 않음

테스트 5.2 — 기기 인가 흐름 악용

# 입력이 제한된 기기용 Device Flow는 피싱에 악용될 수 있음
POST /device/code
client_id=LEGITIMATE_CLIENT

# 피해자가 verification_uri에 입력할 user_code 반환
# 공격자는 이를 사회공학에 사용함:
# "accounts.target.com/activate에서 코드 XXXX-XXXX를 입력하세요"

전체 OAuth 테스트 체크리스트

인가 전 단계

인가 요청 테스트

토큰 교환 테스트

토큰 보안 테스트

계정 연결 테스트


실제 OAuth 버그 사례

버그 유형플랫폼영향보상금
redirect_uri 우회Facebook계정 탈취25,000달러
state 매개변수 누락AirbnbCSRF → 계정 탈취8,000달러
코드 재사용Microsoft재전송 공격15,000달러
JWT none 알고리즘Auth0인증 우회10,000달러
이메일 혼동 계정 탈취Slack계정 탈취20,000달러
PKCE 미강제다수코드 가로채기5,000달러 이상

테스트 도구

OAuth 보안 테스트 자동화

KENSAI의 AI 기반 스캐너는 OAuth 구현에서 인가 코드 가로채기, PKCE 우회, 토큰 취약점과 계정 탈취 시나리오를 자동으로 테스트합니다.

무료 스캔 시작 →

보안은 선택이 아닙니다.

🗡️ KENSAI 팀

관련 기사

Smart Slider WordPress CVE로 웹사이트 50만 개 영향, TP-Link 및 Cisco IOS 패치 인터폴이 악성 IP 주소 45,000개를 해체하고 Google이 취약점을 수정 CVE-2026-28756: Zohocorp ManageEngine Exchange Reporter Plus의 크로스 사이트 스크립팅(XSS)