유연하고 확장 가능한 프레임워크로서, OAuth의 보안 고려 사항은 많은 요소들에 따라 달라진다. 다음 절들은 2.1절에서 설명된 세 가지 클라이언트 프로파일
즉
- 웹 애플리케이션
- 사용자 에이전트 기반 애플리케이션
- 네이티브 애플리케이션
에 초점을 맞춘 보안 지침을 구현자에게 제공한다.
OAuth의 포괄적인 보안 모델과 분석, 그리고 프로토콜 설계에 대한 배경은 [OAuth-THREATMODEL]에 제시되어 있다.
10.1 클라이언트 인증
이 문단이 설명하는 시점은 인가 서버가 클라이언트 인증을 요구하거나 판단해야 하는 모든 순간을 포괄적으로 설명
인가 서버는 클라이언트 인증을 목적으로 웹 애플리케이션 클라이언트에게 클라이언트 자격 증명을 설정한다.
- 클라이언트 비밀번호 보다 더 강력안 인증 수단을 고려하라
- 웹 애플리케이션 클라이언트는 클라이언트 비밀번호 잘 관리해라
인가 서버는 웹 애플리케이션 클라이언트가 "나 진짜 등록된 클라이언트야"라고 증멸할 수 있도록 client_id, client_secret 같은 클라이언트 자격 증명을 발급하고 관리함.
여기서 말하는 웹 애플리케이션 클라이언트
- 서버에서 동작하는 앱 -> Spring boot 백엔드, Node.js 서버, Django 서버
이것들은 비밀 정보를 서버에 안전하게 저장할 수 있는 클라이언트
클라이언트 비번 보다 더 강력한 인증 == client_secret만으로 부족하다는 거 이것도 결국 비밀번호이니 더 강력한 방법 사용해라라는 거 문서에는 안 나와있지만 mTLS, private_key_jwt, client assetion, 인증서 기반 인증
인가 서버 클라이언트 인증 목적으로
- 네이티브 애플리케이션
- 사용자 에이전트 기반 애플리케이션 클라이언트
에 클라이언트 자격 증명을 발급해서는 안 됨
다만, 인가 서버는 특정 장치에 설정된 특정 네이티브 애플리케이션 클라이언트의 개별 설치에 한해서는 클라이언트 비밀번호 또는 기타 자격 증명 발급 가능
왜? 네이티브/사용자 에이전트 기반 앱 클라이언트에게 자격 증명을 주면 안 되나 == 비밀을 숨길 수 없기 때문
네이티브 == 기기들, 사용자 에이전트 기반 애플리케이션 클라이언트 == SPA
-> client_secret을 사용한 '클라이언트 인증' 하지마라는 거임 즉, client_secret 기반 인증 그래서 등장한 게 PKCE, Redirect URI 검증, Public Client
특정 장치 -> 이 경우에는 인가 서버가 강한 전제를 추가할 수 있기 때문 ex ) 디바이스 ID, 하드웨어 보안 영역, OS 키체인 등
이 secret은 이 앱의 모든 복사본이 아니라 이 기기 1대의 이 설치본에서만 의미있음
클라이언트 인증이 불가능한 경우
- 인가 서버 -> 클라 신원 검증을 위해 다른 수단 이용
- ex -> 클라 리다이렉션 URI의 등록 요구, 리소스 소유자가 신원을 확인하도록 하는 방법
- 유효한 리다이렉션 URI는 리소스 소유자의 인가를 요청하는 단계에서 클라이언트의 신원을 검증하기 충분하지는 않지만 리소스 소유자의 인가를 획득한 이후 자격 증명이 위조된 클라이언트에게 전달되는 것을 방지 사용
유효한 리다이렉션 URI..
이 말이 공격자가 정상적인 client_id를 쓰고 리다이렉션 URI만 바꿔서 요청을 할 수 있는 겨 그러면 인가 코드를 통해 토큰을 공격자가 받음 redirect_rul 등록/검증을 통해 token을 주지 않는 다라는 말
인가 서버는 인증되지 않은 클라이언트와 상호작용하는 데 따른 보안 영향을 고려해야 하며, 이러한 클라이언트에게 발급되는 기타 자격 증명(예: 리프레시 토큰)의 잠재적인 노출을 제한하기 위한 조치를 취해야 한다.
10.2 클라이언트 사칭
어떤 앱(클라이언트)이 client_secret 같은 비밀번호를 잘 지키지 못하면 또는 애초에 비밀정보를 가질 수 없는 앱이면 나쁜 앱이 "나 그앱이야"라고 사칭해서 사용자 데이터(보호된 리소스)에 접근할 수 있음
그래서 인가 서버는
- 가능한 경우 항상 클라 인증하셈 == 기밀 클라이언트 반드시 인증
만약 클라이언트의 특성상 인가 서버가 클라 인증할 수 없는 경우 == 모바일 앱, SPA
- 모든 리다이렉션 URI의 응답 반드시 요구
- 리소스 소유자 개입해사 클라이언트 식별하게 하셈 -> 사용자에게 앱 이름, 앱 출처, 요청 권한 이 정보를 명확히 보여주셈
인가 서버는 무조건 리소스 소유자한테 명시적인 인증 강제하셈 즉, 로그인 없이 자동 승인 x, 조용히 동의 처리 x -> 반드시 로그인, 동의 화면 그 화면에서 어떤 앱인지, 얼마나 오래 쓰는지, 어떤 권한을 요청하는지 꼭 보여줘라 -> 리소스 소유자 검토 후 요청 승인 및 거부
인가 서버 -> 클라 인증 하지 않거나 어떤 앱이 계속 인가 요청을 보내는데 사용자한테 묻지도 않고 서버가 자동 승인 이거 하지 마라
정리
- 클라이언트 인증은 가능하면 무조건 하셈
인증이 불가능한 앱이라면
- redirect_uri 엄격히 제안
- 사용자에게 앱 정보를 명확히 보여주기
- 매번 사용자 동의를 거치고 조용히 자동 승인 하지마라
10.3 액세스 토큰
액세스 토큰은 전송 중과 저장 시에 반드시 기밀로 유지되어아함 인가 서버, 해당 엑서스 토큰이 유효한 리소스 서버들, 해당 액세스 토큰이 발급된 클라이언트만 공유되어야함
- 1.6 절과 같이 TLS를 통해서만 전송되어야함
TLS를 강제하는 이유는 액세스 토큰이 평문 HTTP로 전송되면 중간자가 그대로 훔쳐감
암묵적 방식에서 URI 프래그먼트로 액세스 토큰 전달되는데 -> 인가 되지 않는 놈에게 노출될 수 있음
인가 서버는 인가 되지 않은 당사자가 유효한 액세스 토큰을 생성, 수정 또는 추측할 수 없도록 보장해야함
충분히 길고, 랜덤하고, 예측 불가해야함
클라이언트는 최소한 scope로만 액세스 토큰을 요청하셈 인가 서버는 요청된 범위를 어떻게 허용할지 결정할 때 클라이언트 신원을 고려해라 요청된 것 보다 더 작은 권한을 가진 액세스 토큰을 인가 서버는 발급 가능
이 문서에서는 리소스 서버가 특정 클라이언트에 의해 제시된 액세스 토큰이 인가 서버에 의해 해당 클라이언트에게 발급된 것인지를 보장하기 위한 어떠한 방법도 제공하지 않음
즉, 리소스 서버는 이 토큰이 이 클라이언트용인지 알 수 없다라는 거
10.4 리프레시 토큰
인가 서버
- 웹 애플리케이션 클라이언트
- 네이티브 애플리케이션 클라이언트
에 대해 리프레시 토큰을 발급 가능
RFC 6749 에서는 사용자 에이전트 기반 애플리케이션 리프레시를 발급하면 안된다고 강제 하지는 않았지만 원래는 안됨
but 6749가 절대 금지라고 안 함 and 보안 전제를 추가하면 발금 가능
이 클라 금지하는 이유가 -> client_secret을 지키지 못함 그리고 보통 저장할 때 sessionstorage, localstorage, indexed DB를 쓰는데 XSS 한 번 이면 토큰 끝..
그래서 설계는 refresh 안주고 AccessToken 짧게 감 만료되면 재로그인 or silent auth
(요즘 Authorization Code + PKCE + no refresh token)
네이티브 -> refresh 가능 ㄷ대신 PKCE, rotation, secure storage(keyChian, keystore)
기밀 클라 -> refreshg 정석 OK
리프레시 토큰 전송 중과 저장 시에 반드시 기밀로 유지됨어야함
- 인가 서버와 해당 리프레시 토큰이 발급된 클라 사이에서만 공유되어아함.
- 인가 서버는 리프레시 토큰과 해당 토큰이 발급된 클라이언트 간의 바인딩을 반드시 유지해야함 -> 이 말이 이 클라이언트 전용 열쇠를 제대로 알고 주라
만약 클라 인증이 불가능하면 -> 인가 서버는 리프레시 토큰 오용을 탐지하기 위한 다른 수단을 배치해라 == 이건 저번에 한 거 액토 발급하면 새 리프레시 토큰 발급해서 원래 있던 거는 무효화 (refresh token rotation)
인가 서버는 인가되지 않은 당사자가 유효한 리프레시 토큰을 생성, 수정, 또는 추측할 수 없도록 반드시 보장해야함
10.5 인가 코드
인가 코드 TLS를 써서 전송 == HTTPS로 보내라 -> 왜? 인가 코드는 브라우저 리다이렉션을 통해 이동해서 그럼 의도치 않게 노출되는 거임 이게 브라우저 방문 기록, 다른 사이트로 이동할 때 Referer 헤더, 브라우저 플러그인, 공유된 화면 이런 걸로 가능
인가 코드는 평문 베어러 자격 증명(plaintext bearer credentials)로 동작함 -> 이거 탈취 당하면 공격자가 정상 클라이언트 처럼 토큰 교환이 가능함..
- 클라이언트가 인가 코드를 사용자 인증 근거로 삼는다면 -> 클라이언트 리다이렉션 엔드포인트는 TLS(HTTPS)
인가 코드는 반드시 짧은 수명 가져야함 && 한 번만 사용 가능해야함
- 하나의 인가 코드를 액세스 토큰으로 교환하는 걸 여러번 시도 한다면 인가 서버는 이미 발급된 모든 액세스 토큰 철회하려고 하셈
클라이언트를 인증할 수 있는 경우 인가 서버는 반드시 클라이언트 인증을 해라 또한 해당 인가 코드가 동일한 클라이언트에게 발급된 건지 검사를 꼭 해라
10.6 인가 코드 리다이렉션 URI 조작
인가 코드 그랜트 유형
- 인가를 요청할 때 클라는 -> "redirect_uri" 매개변수를 통해 리다이렉션 URI를 지정했음
정상적인 상황을 본다면
1. 클라이언트가 인가 서버에 요청함 -> 로그인 끝나면 이 주소로 다시 보내줘요"
redirect_uri = https://client.com/callback
2. 사용자가 로그인 + 동의
3. 인가 서버가 브라우저를 이렇게 돌려보냄
https://client.com/callback?code=ABC
이제 공격자가 하는 걸 보자 -> 공격자가 redirect_uri 값을 바꿀 수 있다면?
1. 공격자는 정상 클라이언트인 척 준비 -> 합법적인 서비스에 자기 계정 하나 생성, 정상 인가 요청 URL을 확보함
2. redirect_uri를 공격자 주소로 바꾼 링크를 만듦
원래
redirect_uri=https://client.com/callback
조작 후
redirect_uri=https://attacker.com/callback
3. 피해자를 속여서 이 링크를 클릭하게함 -> 이메일, 메신저, 피싱 페이지 등, 피해자는 "정상 서비스 로그인" 화면을 봄
- client_id도 진짜, 동의 화면도 정상, 피해자는 의심 없이 승인
4. 인가 서버의 실수
인가 서버가 redirect_uri를 제대로 검증하지 않으면
https://attacker.com/callback?code=ABC
-> 인가 코드가 공격자 서버로 날아감
5. 공격자는 인가 코드로 토큰 교환
공격자는 이제 그냥 이렇게 함
POST /token
code=ABC
-> 피해자 권한의 액세스 토큰 획득
-> 그 토큰을 자기 계정과 연결
위를 막기 위해 == 공개는 무조건 해라, 기밀은 권장 사항이긴 함
1. redirect_uri 사전 등록 -> 인가 요청시 redirect_uri 검증
2. 토큰 요청시 redirect_uri 재검증 -> 인가 코드 발급 때 사용하 redirect_uri == 토큰 교환 때 전달된 redirect_uri이 완전히 동일한지 보기
10.7 리소스 소유자 비밀번호 자격 증명
이 자격 증명은 OAuth 이전 시스템 == HTTP Basic 인증, 아이디/비번 직접 전송을 말함 -> 이 시스템을 OAuth로 바꾸기 어려움 그래서 마이그레이션 할 때 즉, 일단 기존 방식 유지하면서 OAuth로 옮기자 용도로 허용된 방식
- 이게 위험 하긴 한데 위험을 줄이자
- 기존 방식(Oauth 아님) -> 클라가 비번 저장 후 비번으로 계속 로그인
- 위를 방지해서 클라가 비번을 한 번 받아서 인가 서버에 전달 이후엔 토큰 사용 형태로 == Password Grant
- 그래서 비번을 계속 쓰는 위험을 줄어드는데,, 하지만 비번을 클라가 본다는 사실 자체는 변하지 않음
OAuth는 원래 만들고 싶었던 게 클라이언트 절대 비번 몰라야한다 X -> 비밀번호는 인가 서버만 알아야한다 그런데 위에서 말한 Password Grant는 OAuth가 피하려던 구조 그대로 유지함 == 사용자 -> 비번 -> 클라이언트 -> 인가 서버라서
그래서 비밀번호 안티패턴을 그대로 유지한다가 다른 그랜트 보다 본질적으로 더 위험하다.. 어떤 위험이 있냐면
1. 클라이언트가 악의적일 수 있다는 거 -> 비번 저장, 다른 서비스에서 재사용, 몰래 계정 탈취
2. 실수로 비번 새어나갈 수 있음 -> 로그 파일, 에러 로그, 디버깅 출력, 모니터링 도구
그리고 리소스 소유자가 인가 과정에 대한 통제권을 가지지 못한다라는 문제점
- OAuth의 정상흐름을 보면 사용자가 직접 어떤 클라인지, 어떤 권한, 승인 및 거부를 확인 및 선택하는데
- password Grant -> 사용자는 그냥 아이디/비번 입력임 그 이후 어떤 scope가 요청됐는지 얼마나 오래쓰이는 토큰인지 무엇에 쓰이는지 전혀 모름 즉, 사용자는 로그인만 했지 권한 위임을 통제하지 못함
- 그래서 scope 문제임 -> 클라가 필요 이상으로 넓은 scope 요청 가능 그래서 인가 서버가 토큰의 scope와 수명을 인가 서버가 매우 신중하게 제한해야한다 라는 거
인가 서버와 클라는 이 그랜드 유형 사용을 최소화하고 다른 거 사용하셈
10.8 요청 기밀성
액토, 리토, 리소스 소유자 비번, 클라이언트 자격 증명 -> 평문으로 보내지 마라
다음과 같이 보내셈
https://example.com/token
Authorization: Bearer ABC123
- HTTPS(TLS)
state,scope -> 이 매개변수에는 민감한 클라 정보나 리소스 소유자 정보가 평문으로 포함하지 마라
- 이거 URL에 실릴 가능성이 높음그래서 민감한 정보 넣으면 노출될 수 있다라는 거임
비밀이어야 하는 값들은 절대 평문으로 보내지 말고 state, scope 같은 값에도 민감한 정보는 넣지마라 라는 핵심
10.9 엔드포인트 진위 보장
중간자 공격(MITM)이란?
- 클라이언트가 인가 서버랑 통신 중이라고 생각해보자 그런데 중간에 중간에 공격자가 끼어들면?
- 이 상태에서 공격자는 -> 요청 내용 엿 보기, 토큰 탈취, 응답 조작, 가짜 서버 행세 == 이게 중간자 공격(Man-in-the-Middle)
그래서 인가 서버는 인가 엔드포인트 + 토큰 엔드포인트로 오는 모든 요청에 TLS를 반드시 사용 여기서 말하는 서버 인증을 사용하는 TLS 이 말이 핵심인데 단순히 HTTPS가 안전하다라는 건 아님 TLS에는 두 가지가 있음
1. 암호화
2. 서버 인증(이 서버가 진짜냐?)
RFC 2818은 -> TLS + 서버 인증서 검증이 포함된 HTTPS를 말함 즉, 암호화만 하면 끝 X, 서버 신원 확인까지 포함해야함 O
클라이언트는 HTTPS라고 써 있어도 아무 서버나 믿지마라 진짜 인가 서버인지 확인해라 이거 안 하면 공격자가 가짜 인증서, 가짜 서버, 프록시 서버를 내밀어도 클라가 그냥 믿어버리면 가짜 인가 서버에 client_secret, authorization code, access token 받음(가짜) RFC6125 -> 클라는 반드시 인증서가 신뢰 가능한 CA에서 발급되었는지, 인증서의 도메인 이름이 요청한 서버와 일치하는지, 인증서가 만료/폐기되지 않았는지를 확인
핵심 -> 인가 서버와 통신할 떄는 반드시 TLS를 쓰고 클라이언트는 그 TLS가 진짜 인가 서버인지 꼭 확인해라
10.10 자격 증명 추측 공격
인가 서버 -> 공격자가 액토, 인가 코드, 리토, 리소스 소유자 비밀번호, 클라이언트 자격 증명을 추측하지 못하도록 반드시 방지
- 좀 쉽게 말하면 공격자가 토큰을 하나씩 찍어보거나, 자동화 프로그램 대입, 규칙을 추정해서 만들어보는 방식으로 유효한 값에 맞히는 일이 절대 있어서는 안 된다는 거임 토큰 == 랜덤하고 예측 불가능해야함
어느 정도로 안 맞혀져야 하냐를 본다면 2^-128이하 가능하면 2^-180 이하
- 2^-128은 가능한 경우의 수가 2^128개 == 공격자가 무작위로 하나 찍었을 때 맞을 확률 -> 우주가 끝날 때까지 시도해도 못 맞힐 수준
최종 사용자 자격 증명 == 사용자 비번, PIN, OTP, 사람이 직접 입력하는 값 이건 랜덤 128비트 처럼 만들 수가 없음 사람이 기억해야하니.. 그래서 다른 보호 수단이 필요 -> 서버 측 보호(bcrypt, argon2), 솔트 사용, 평문 저장 x .. 등 이걸 인가서버가 잘 해라
10.11 피싱 공격
OAuth를 쓰면 보통 다음과 같은 흐림이 반복됨
1. 어떤 서비스에서 -> 구글 로그인, 카카오 로그인 클릭
2. 갑자기 다른 사이트로 이동
3. 거기서 아아디/비밀번호 입력
-> 너무 흔해지면 사용자는 아 또 로그인 페이지네 그냥 치면 되겠지 생각함 -> 여기서 피싱이 먹힘
서비스 제공자는 최종 사용자에게 피싱 위험을 교육하고 사이트 진위를 쉽게 확인할 수 있도록 해야함
- 이 서비스는 accounts.example.com에서만 로그인합니다
- 로그인 화면에 고정된 도메인 안내
- 이메일/알림으로 "우리는 비밀번호를 묻지 않습니다"
- 브라우저 주소창 확인 유도
등,,
-> 사람을 교육하는 것도 보안의 일부
클라이언트 개발자는 사용자 에이전트와 상호작용 방식에 따른 보안 영향을 고려해야함
- 사용자 에이전트 종류
- 외부 사용자 에이전트
- 임베디드 사용자 에이전트
피싱 위험을 줄이기 위해 인가 서버는 모든 사용자 상호 작용 엔드 포인트에 TLS를 반드시 사용해야함
10.12 교차 사이트 요청 위조(CSRF)
CSRF(Cross-Site Request Forgery)는 사용자가 의도하지 않았는데 이미 로그인된 상태를 이용해서 서버가 정상 요청이라고 착각하게 만드는 공격
핵심 포인트 -> 사용자는 로그인 상태, 브라우저는 자동으로 쿠키를 보냄, 서버는 쿠키만 보고 '아 정상 사용자네'라고 믿음
OAuth에서 CSRF가 어떻게 터지냐
정상 흐름(공격 없음)
1. 사용자 → 클라이언트
2. 클라이언트 → 인가 서버 (인가 요청)
3. 사용자 로그인 & 동의
4. 인가 서버 → 클라이언트 redirect_uri
?code=ABC&state=XYZ
5. 클라이언트: “이 요청은 내가 시작한 게 맞네”
CSRF 공격 흐름(문서가 말하는 상황)
공격자가 하는 일
1. 공격자는 자기 계정으로 OAuth 인가를 먼저 받아둠
- code = ATTACKER_CODE
- 또는 access_token = ATTACKER_TOKEN
2. 공격자는 피해자에게 이런 링크를 보냄:
https://client.example.com/callback?code=ATTACKER_CODE
3. 피해자가 로그인된 상태에서 이 링크를 클릭함
클라이언트가 방어를 안하면
- 브라우저는 자동으로 피해자 쿠키를 보냄
- 클라이언트는 이렇게 착각함 -> 오 인가 코드 왔네 이건 피해자가 로그인해서 동의한 결과겠지?
이 코드는 공격자의 인가 코드 결과적으로 피해자 세션 <-> 공격자 리소스 연결됨
문서에서 나온 예시가 -> 피해자의 은행 계좌 정보를 공격자가 제어하는 보호된 리소스에 저장
- 사용자는 내 계좌 조회한다고 생각했는데 실제로는 공격자 계좌에 작업이 수행됨
그래서 클라이언트는 CSRF 보호를 해야한다라는 거임 -> 리다이렉션 URI로 들어오는 요청이 "이 사용자가, 이 브라우저에서, 내가 시작한 요청인지 반드시 확인해라
그걸 어떻게 하냐? -> state
1. 인가 요청 시작할 때
- 클라이언트가 랜덤 값 생성
state = "X8s9dKQ2..."
이 값은 : 추측 불가능, 세션에 저장됨
2. 인가 서버로 보낼 때
GET /authorize?
response_type=code
&client_id=...
&state=X8s9dKQ2
3. 인가 서버가 돌려줄 때
https://client.example.com/callback?
code=ABC
&state=X8s9dKQ2
4. 클라이언트 인증
요청에 포함된 state == 세션에 저장된 state ?
같으면 정상, 다르면 CSRF 공격 -> 거부
왜 state가 사용자 에이전트의 인증 상태와 바인딩이냐?
- 이 요청이 이 브라우저, 이 로그인 세션, 내가 직접 시작한 Oauth 흐름에서 나온 게 맞는지 확인하는 장치
쿠키만으로는 부족하니까 쿠키 + state를 묶어서 확인하는 거
인가 서버의 인가 엔드포인트도 CSRF에 취약함 -> 공격자가 몰래 인가 요청을 자동으로 보내서 사용자가 모르는 사이에 동의 버튼을 누르게 만드는 공격 그래서 인가 서버도 -> 사용자 상호작용 강제, 자동 승인 금지, CSRF 방어 로직 필요
한 문장으로 요약하면
OAuth에서 CSRF는 “공격자의 토큰/코드를 피해자 세션에 끼워 넣는 공격”이고, 이를 막기 위해 클라이언트는 반드시 state를 사용해야 한다.
10.13 클릭재킹
클릭 재킹 -> 사용자가 어떤 버튼을 누른 줄 알았는데, 실제로는 다른 페이지(인가 서버)의 승인 버튼을 누르게 만드는 공격
공격 흐름을 보자 -> 등장인물 사용자(피해자), 공격자 사이트, 인가 서버(Authorize 페이지)
공격자의 준비
1. 공격자는 "정상 클라이언트"로 등록해둠
- OAuth client_id를 받아놓음(겉으로는 정상 앱처럼 보이게)
2. 공격자 사이트에 투명한 iframe을 깔아둠
- 공격자 페이지 화면 구조가 다음과 같음
- 위(보이는 화면) : "쿠폰 받기" 같은 가짜 버튼
- 아래 (보이지 않는 층) : 인가 서버의 승인 페이지(Authorize)를 iframe으로 띄움(투명하게 만들어서 사용자는 못 봄)
- 그리고 공격자는 가짜 버튼 위치를 Authorzie 버튼 위치랑 정확히 겹치게 맞춰둠.
피해자가 당하는 과정
3. 사용자는 공격자 사이트에서 "쿠폰 받기"를 클릭
- 사용자 입장 -> 쿠폰 받기 눌렀다~ -> 하지만 실제로는 투명 iframe 안의 Authorzie 버튼을 클릭한 것
4. 결과 : 사용자가 모르게 승인이 되어버림
- 사용자가 "허용"을 의도하지 않았는데 공격자의 클라이언트가 권한을 얻어버림
- 이게 최종 사용자의 인지 없이 접근을 승인하도록 속인다
이게 OAuth에서 승인 버튼은 -> 이 앱이 내 정보 접근해도 된다는 의미이기에 클릭재킹으로 승인되면 공격자의 앱이 사용자 데이터 접근 권한을 획득할 수 있음
그럼 어떻게 막냐?
1. 네이티브 앱은 "외부 브라우저"를 써라
- 앱 안 WebView는 UI조작/위장 위험이 커서
- 외부 브라우저(Chrome/Safari)로 열면 사용자 입장에서 "지금 브라우저에서 로그인/승인 중"을 인지하기 쉬움
- 보호 기능도 많음
2. 인가 서버가 X-Frame-Options로 iframe 자체를 막아라
- 이 헤더는 " 내 페이지를 iframe에 넣지마"라고 브라우저에게 지시하는거
- X-Frame-Options : DENY -> 어떤 사이트든 iframe으로 내 페이지 못 띄움
- X-Frame-Options : SAMEORIGIN -> 같은 출처(도메인)에서만 iframe 허용(다른 사이트는 iframe 불가)
즉, 공격자 사이트가 iframe으로 인가 페이지 띄우는 걸 차단함
3. 구형 브라우저는 자바스크립트로 "프레임 탈출"
예 ) "내가 iframe 안에서 실행 중이면 top으로 튀어나가라" 같은 코드 -> 하지만 RFC에서는 구형 브라우저에서는 완벽하게 안 먹힐 수도 있다라는 거
한 줄 정리
클릭재킹은 공격자가 인가 페이지를 투명 iframe으로 숨겨 띄워놓고, 사용자가 다른 버튼을 누르는 것처럼 속여 ‘Authorize’를 클릭하게 만드는 공격이며, 인가 서버는 X-Frame-Options로 iframe을 차단해 이를 막을 수 있다
10.14 코드 주입 및 입력 검증
코드 주입(code Injection)이란?
- 사용자가 넣은 값(입력값)을 -> 그냥 믿고 실행하거나 처리해서 프로그램의 의도한 동작이 바뀌는 공격
- 즉, "데이터"로 써야할 값을 "코드"처럼 해석해 버리는 상황
왜 위험?
- 서버/앱 내부 접근
- 데이터 탈취 및 변조
- 서비스 마비
- 원래 없던 동작 실행
즉, "입력 하나 잘못 받았을 뿐인데, 앱 전체가 조종 당할 수 있다.
OAuth에서는 외부에서 값이 많이 들어옴 -> 입력값을 그대로 쓰면 바로 공격 지점이 됨
예시(redirect_uri, state)
1. redirect_uri 검증 안함
-> 아 그냥 이걸로 redirect_uri로 돌려주면 되겠네 -> 인가 코드/ 토큰이 공격자 서버로 날아감
2. state를 그대로 사용
state=<script>alert(1)</script>
만약 클라이언트가 -> state 값을 고르게 출력하거나 HTML에 그대로 렌더링 하면 XSS(스크립트 주입) 발생
그래서 인가 서버와 클라는 -> 수신한 모든 값을 반드시 정제(sanitize) 및 검증(validate)하라
한 줄 정리
OAuth에서는 외부에서 들어오는 값이 곧 보안 로직에 사용되기 때문에, 인가 서버와 클라이언트는 특히 state와 redirect_uri 같은 매개변수를 절대 신뢰하지 말고 반드시 정제·검증해야 한다.
10.15 오픈 리다이렉터
오픈 리다이렉터(Openm Redirector)란?
- URL 파라미터에 들어온 값을 검증 없이 그대로 사용해서 사용자를 그 값이 가리키는 곳으로 자동 이동시켜주는 엔드포인트
redirect_uri=https://client.com/redirect?next=https://evil.com
-> redirect_uri 값 안에 들어간 -> /redirect?next = ... -> 클라이언트의 오픈 리다이렉터
실제 흐름
1. 공격자가 인가 요청을 보냄
GET /authorize?
response_type=code
&client_id=client123
&redirect_uri=https://client.com/redirect?next=https://evil.com
2. 인가 서버 판단
- client.com이 등록돼 있음 -> OK
- 내부 쿼리 파라미터까지 안 봄
3. 인가 서버 리다이렉트
https://client.com/redirect?next=https://evil.com&code=ABC123
4. 클라이언트 서버 동작
302 Location: https://evil.com?code=ABC123
-> 인가 코드 탈취
그래서
- 인가 서버 -> redirect_uri는 완전 일치 비교, 클라이언트 리다이렉션 엔드포인트가 오픈 리다이렉터인지 고려해라
- 클라이언트 -> 외부 URL로 재리다이렉트 X, 허용된 경로만 이동 O
10.16 암묵적 흐름에서 액세스 토큰을 사용한 리소스 소유자 가장
암묵적 흐름을 사용하는 공개 클라이언트의 경우 -> 이 문서에서 액토이 어떤 클라이언트에 발급되었는지 클라이언트가 판다할 수 있는 방법을 제공하지 않는다.
즉, 액세스 토큰은 보통 -> 누구의 리소스에 접근 가능한가?만 있지 이 토큰이 어떤 클라이언트를 위해 발급됐는지 정보가 없음 -> 이 토큰이 A 클라이언트 용인지, B클라이언트용인지 클라이언트는 알 수 없음
공격 시나리오 == 토큰 재사용 공격
1. 공격자 준비
- 공격자는 자기 악성 클라이언트를 만듦
- 사용자를 속여서 자기 클라이언트에 로그인 하게 함
- 그 결과 -> 공격자는 정상적으로 액세스 토큰을 하나 얻음
2. 공격자가 토큰을 들고 정상 클라이언트로 감
정상 공개 클라이언트 (SPA)
공격자는 이렇게 말함 -> 나 로그인된 사용자야 여기 토큰 있어
Authorization: Bearer ATTACKER_TOKEN
3. 정상 클라이언트의 치명적인 착각
정상 클라는 다음과 같이 생각함 -> "유효한 액세스 토큰이네?" -> 이 토큰을 가진 사람 -> 리소스 소유자겠지
하지만 틀림 이 토큰은 공격자 클라이언트용 그런데 암묵적 흐름에서는 구분 불가
4. 결과
공격자는 -> 피해자 행세, 피해자 권한으로 데이터 조회/수정
정상 클라이언트는 -> 공격자에게 피해자 정보 노출
문장에서 나온 토큰 바꿔치기 문장해석 == 이제는 토큰 바꿔치기
공격 흐름
1. 공격자는 이미 자기 토큰을 하나 가지고 있음(자기 계정으로 accesstoken 하나 발급)
2. 정상 사용자는 정상 클라이언트로 로그인
3. 토큰 바꿔치기 -> 공격자가 응답 이후 클라이언트가 읽기 전에 자기 액세스 토큰으로 바꾸는 것(프래그먼트)
4. 클라이언트는 토큰의 출처를 알 수 없음
5. 결과 -> 공격자는 리소스 소유자 가장 성공 그러므로 사용자 정보 엉킴, 공격자 리소스에 데이터 저장, 권한 오남용
- 암묵적 흐름에서는 공격자가 인가 서버의 응답에서 토큰을 쉽게 바꿔치지 할 수 있다라는 말이 토큰이 프래그먼트로 전달됨 JS로 쉽게 조작 가능
#access_token=REAL_TOKEN ↓ #access_token=ATTACKER_TOKEN
-> 클라이언트 눈치 못 챔
네이티브 앱도 위험하다고 함
- 백 채널로 전달되는 액세스 토큰에 의존하는 서버도 위험
- 상황 -> 모바일 앱이 액세스 토큰을 서버에 보내서 "이 사용자 누구야?"라고 묻는 구조
- 공격 -> 공격자가 조작된 앱을 만들어 훔친 토큰을 서버로 전송
- 서버 -> 토큰 유효함 -> 사용자 OK
토큰이 어떤 앱용인지 확인 불가
그래서 액세스 토큰을 사용자 인증 수단으로 쓰면 안 된다. 즉, “액세스 토큰 있으면 = 로그인된 사용자” 이게 아니라는 거임 토큰은 리소스 접근 권한이지 사용자 신원 증명서가 아님
그래서 RFC에서는 서드파티 로그인 같은 인증 용도로는 추가 보안 메커니즘 없이 암묵적 흐름을 쓰면 안된다
- 추가 보안 메커니즘 예시
- audience(aud) 제한
- token binding
- -> 이게 나중에 OIDC, PKCE, code flow로 이어짐
한 줄 핵심
암묵적 흐름에서는 “이 액세스 토큰이 우리 클라이언트용인지”를 클라이언트가 알 방법이 없어서,
공격자가 자기 토큰을 들고 와서 사용자 행세를 할 수 있다.
'RFC > OAuth 2.0' 카테고리의 다른 글
| [OAuth 2.0] Spring Security (0) | 2026.02.03 |
|---|---|
| [OAuth 2.0] 개발 프로젝트 (0) | 2026.02.03 |
| [OAuth 2.0] 9. Native Applications (0) | 2026.01.13 |
| [OAuth 2.0] 8. Extensibility(확장성) (0) | 2026.01.13 |
| [OAuth 2.0] 7. Accessing Protected Resources (1) | 2026.01.13 |