개요
PKCE
(픽시)의 개념에 대해 정리한다.PKCE
를 사용하면 뭐가 좋아지는지 정리한다.- 실제 HTTP 요청응답에서는 어떤 모양인지도 정리한다.
PKCE(픽시)란?
- RFC7636으로 정의되어 있다. (그렇게 길지 않다. 시간되면 읽어보자.)
- Authorization Code flow 의 확장팩이다.
- Authorization Code Grant를 사용하는 OAuth 2.0 public client는
인가 코드 가로채기 공격(the authorization code interception attack)
에 취약한데 이 부분을 해결하기 위해 나온 방법이다. - OAuth 2.0에서는 옵션이지만 OAuth 2.1부터는 필수가 되는 것 같다. 참고로 OAuth 2.1은 2024년 12월 시점에도 검토중인 상태이다. (https://oauth.net/2.1/)
인가 코드 가로채기 공격 (The Authorization Code Interception Attack)
인가 코드 가로채기 공격의 구조도이다. 스마트폰과 같은 디바이스에서 앱스토어를 통해 설치된 악의적인 어플리케이션이 다른 어플리케이션(OAuth 2.0 App) 으로 발행된 인증코드를 가로채는 공격이다.
(출처: https://datatracker.ietf.org/doc/html/rfc7636)
PKCE를 사용한 인증 흐름
- OAuth 클라이언트가 인가 코드를 요청할 때 자신이 만든 특정한 값(code_verifier)를 함께 인가서버로 보낸다.
- 인가 서버로부터 인가 코드(Authz code)를 받은 후에 억세스 토큰을 요청할 때도 code_verifier를 함께 보낸다.
- 토큰 발행 서버는 code_verifier 값을 함께 확인해서 정당한 값일 때만 토큰을 발행한다.
- 이렇게 하면 뭐가 좋아지는가? 설령 제삼자 어플리케이션이 인가 코드를 얻어내도, code_verifier는 이 값을 발행한 정당한 원래의 OAuth 클라이언트 밖에 모르기 때문에 결과적으로 억세스 토큰를 얻어낼 수 없다!
보안상 의의, 중요 포인트
- 문제가 있을 경우 인가 토큰을 받은 후에 액세스 토큰을 발행하는 곳에서 플로우를 멈출 수 있다.
- 정보보안 CIA관점에서 말하자면 기밀성(Confidentiality)이 강해진다고 볼 수도 있겠다.
- 픽시는 OAuth 토큰을 발행할 때까지의 흐름을 안전하게 지키기 위한 구조이다. 거꾸로 말하면 이미 발행된 토큰을 안전하게 보관하는 것은 PKCE와 상관이 없는 다른 문제이다.
PKCE
는 본래 모바일 앱을 위해 개발된 기능이지만 서버 앱들도 모두 적용하도록 권장하고 있다. 이는인가 코드 주입(Authorization Code Injection)
공격에 대한 방어도 되기 때문이다.
PKCE를 사용한 흐름
-
사용자가 App에 사용 요청을하면 App이
Code Verifier
(Secret) 라고 부르는 길이 43~128 의 랜덤한 문자열을 만든다. -
App이
Code Verifier
를 해시 함수(ex sha256)를 통과시켜서 해시 값을 만든다. 이 것을Code Challenge
(Public Hash)라고 부른다. 이 것을 Base64인코딩해서 사용자에게 건네준다. - 사용자가 Auth 서버의 인증 엔드포인트(보통 /auth)로 요청을 보낸다.
- 이 때
response_type
파라메터의 값은code
이다. 인가코드를 얻기 위함을 나타낸다. - client_id로 CLIENT_ID를 지정하고, redirect_uri로 사용자가 인증한 후에 돌아올 URL(앱 서버의 URL)을 지정한다.
scope
은 API가 접근할 scope을 지정한다. state
파라메터는 원래 CSRF보호를 위해서 사용되었지만 현재는 PKCE가 CSRF보호를 제공한다. (서버가 PCKE를 지원하지 않는다면 state는 랜덤한 값을 지정해야 한다.)code_challenge
파라메터를 포함한다. 2번과정에서 만든 값이다.code_challenge_method
파라메터는 해시함수명을 지정한다. 여기서는 s256 이다.
- 이 때
-
제대로 처리 되었다면 사용자는 App으로 리다이렉트된다. 이 리다이렉트URL에는 Auth 서버가 발생한 인가코드와 state값이 포함되어 있다. https://exmaple-app.com/redirect?code=AUTH_CODE_HERE&state=XXXXX 이 때 App은 인가코드 요청에 보냈던 state값이 이 state값이 일치하는지 체크해야 한다. CSRF공격을 막기 위함이다.
- App은 Auth 서버에게 억세스 토큰을 요청한다. 이는 백채널로 이루어진다. 일반적으로 유저는 볼 수 없다. (HTTP 통신을 관찰할 수 있다면 가능하다)
POST https://authorization-server.com/token
grant_type=authorization_code&code=AUTH_CODE_HERE&redirect_uri=REDIRECT_URI&code_verifier=VERIFIER_STRING(PKCE를사용하는경우)&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
- 이 과정에서는 App에 의해
code_verifier
가 Auth 서버로 전달된다. (※ 3번과정에서는code_verifier
의 해시값인code_challenge
가 사용자에 의해서 Auth 서버로 전달되었다. Auth 서버는code_verifier
의 해시값을 구해서code_challenge
와 일치하는지를 확인하는 것으로 2번과정의 App과 현재 요청을 보낸 App이 동일한 App인지 판단한다) - 또한, client_id와 client_secret을 보냄으로서 Auth 서버는 이 클라이언트가 실재한다(정당한 클라이언트로부터의 요청이다)는 것을 알 수 있다.
- 정상적으로 처리되었다면 다음과 같은 응답이 반환된다.
{
"token_type": "Bearer",
"access_token": "Rsejrij3doijfooV3IOa",
"expires_in": 3600,
"scope": "photos",
"refresh_token": "klAHEIRJIODLE44"
}
추기: state와 PKCE에서 제공하는 CSRF대책의 차이
둘다 CSRF 보호를 제공하는데 어떻게 다른가?
state
에 의한 검증에서는 OAuth클라이언트(앱)측에서 자신이 최초에 인가 서버에 제출한 state와, 지정한 redirect_uri에 의해 되돌아온 요청의 state가 동일한지를 체크한다. 클라이언트가 OAuth서버를 검증하는 것이다.PKCE
를 이용한 검증에서는 OAuth 서버가 클라이언트를 검증한다. (정당한 App이 보내온 code_challenge 값과 code_challenge_method 값을 토대로 나중에 억세스 토큰 달라고 요청이 온 App이 제출한 code_verifier를 검증한다.)- 결과적으로는 비슷하지만 다층방어의 개념에서 보면 양쪽 모두 제대로 구현하는 게 좋아보인다.
참고 링크
- OAuth grant types 정리: Burp Academy-OAuth grant types
- https://www.ssemi.net/what-is-the-oauth2/
- https://juniortech.tistory.com/15
- https://qiita.com/TakahikoKawasaki/items/185d34814eb9f7ac7ef3
- https://www.oauth.com/playground/authorization-code-with-pkce.html
- https://datatracker.ietf.org/doc/html/rfc7636
- https://en.wikipedia.org/wiki/OAuth
- state와 PKCE차이: https://qiita.com/ist-n-m/items/992c67b803ff460818ec