📌원문: https://salt.security/blog/oh-auth-abusing-oauth-to-take-over-millions-of-accounts 포스팅을 보고 공부하면서 요약 작성한 글입니다.
OAuth 소개
이 포스팅에서는 일반적인 OAuth 구현 문제를 설명한다. 소셜 로그인 메커니즘과 OAuth 구현에 대한 공격 기법을 소개한다. Grammarly, Vidio, Bukalapak 서비스에서 취약점을 시연한다.
+) 첫 번째 및 두 번째 블로그 게시물도 참고하면 좋을 것 같다.
John 이라는 사용자가 Facebook 계정을 사용하여 Randomsite.com에 연결하려 한다고 가정한다.
Login with Facebook 클릭
Randomsite.com 에서는 페이스북 로그인 페이지로 리다이렉트
https://www.facebook.com/v3.0/dialog/oauth?**redirect_uri=https://randomsite.com/OAuth** &scope=email&client_id=1501&state=[random_value]&response_type=token.
Facebook에서 로그인 및 계정 연결 동의
Facebook은 Randomsite.com에 대한 Secret 토큰을 반환하고 브라우저를 redirect_uri로 리다이렉션
https://randomsite.com/OAuth#token=[secret_token]]&state=[Random_Value]
Secret 토큰을 포함하여 Randomsite.com에 다시 접속하면 URL에서 토큰을 읽어 옴
5번 단계에서 획득한 토큰을 가지고 아래 API를 통해 Facebook과 통신
https://graph.facebook.com/me?fields=id,name,email&access_token=[secret_token].
Facebook에서는 Secret 토큰의 소유자 정보를 반환해 줌
Implicit Grant Type 외에 Explicit Grant Type도 존재하는데, 이는 Randomsite.com가 토큰 대신 코드를 받고 Facebook에 추가 요청을 보내 토큰을 받는 개념이라고 보면 된다.
위 6~7단계에서 서버가 토큰을 받으면 사용자의 신원을 확인하기 위해 Facebook에 API 요청을 보낸다.
이러한 방식은 안전하게 구현되어 있을까??
위 Facebook 문서를 보면, 토큰을 수신한 서버는 이를 검증해야 한다고 기재되어 있다.
요청을 보내기 전 액세스 토큰을 확인하는 것은 개발자의 책임인 셈이다.
Vidio는 온라인 비디오 스트리밍 플랫폼이다. 영화, TV, 라이브 스포츠, 오리지널 프로덕션 등 다양한 콘텐츠를 제공한다. 이 플랫폼의 OAuth 흐름을 살펴보자. 이전에 살펴본 Randomsite.com 예제와 거의 비슷하다.
위 예제에서 숫자 92356은 App ID이다. 애플리케이션이나 웹사이트가 developer.facebook.com에 등록되면 Facebook은 고유한 무작위 App ID를 할당해 준다.
OAuth 요청이 들어 왔을 때 Facebook은 App ID를 보고 어떤 앱이 요청을 했는지 식별한다. Vidio의 경우 92356 이다.
3단계에서 Facebook에 접속하면 Facebook은 Vidio.com에 대한 액세스 토큰을 생성해 준다. 액세스 토큰에는 사용자 정보(이메일)와 Vidio App ID 정보가 모두 포함되어 있다.
아무튼 Vidio.com이 사용자로부터 액세스 토큰을 받으면, com/debug_token API를 통해 액세스 토큰이 App ID 92356에 실제로 생성되었는지를 확인해야 한다.
Facebook은 자동으로 인증을 수행해 주지 않고, 체크하는 것은 온전히 개발자의 몫이다.
그러나 Vidio.com 에서는 토큰을 따로 검증해주지 않고, 공격자가 다른 App ID 에서 생성된 액세스 토큰을 사용할 수 있게 된다.
공격자가 악성 웹사이트를 구축하고 이를 YourTimePlanner.com이라고 가정한다. 공격자는 이 사이트를 쇼핑몰이나 시간관리 앱 같은 합법적인 웹사이트로 위장 게시한다.
그리고 일반 사용자들이 이 웹사이트에 접근해서 로그인하기를 기다린다. 아마 대부분의 사람들은 새로운 웹사이트에 직접 가입하는 대신 소셜로그인 기능을 활용해 로그인 할 것이다.
수천 명의 사람들이 가입했다고 가정하면, 공격자는 실제 사용자에 대한 수천 개의 액세스 토큰을 보유하게 된다. 물론 App ID는 YourTimePlanner의 App ID일 것이고, 예시 토큰을 살펴보면 다음과 같다.
"EAAut0eRcO1QBO9IrSS8xryMLF9wdSuGMGXiVbgJEsMjhLkqRhLYb0z1RcDJ9yw8RTvN0n5VTEfTazaifUYYVcovFutFG3GUP8feKftp4U7gXJaz0lY9wNttKZBqZAP8ZCszUHZAwN8o5iZ BKLSyF7UZAUsITmcs57EXDW44bEGdZBt6rQRkoeGMgtPghfgHrqefbIfBkdyLneTqPMZD"
Facebook 토큰 디버거를 사용하면 액세스 토큰에 대한 모든 정보를 확인할 수 있다.
위 토큰을 Vidio.com에 삽입하면 어떻게 될까? 실제로 계정 탈취가 가능해질 수 있다. 물론 Vidio.com에 계정이 있어야만 가능한 일이다. 최종적으로 아래와 같이 Dan Bro의 계정으로 로그인할 수 있다.
Bukalapak은 1억 5천만 명의 사용자를 보유한 인도네시아에서 가장 크고 유명한 전자상거래 플랫폼 중 하나이다. 이러한 전자상거래 플랫폼에서 계정 탈취 취약점이 존재하는 경우 큰 문제가 될 수 있다.
Bukalapak의 OAuth 인증 구조는 위 Vidio와 거의 동일하다.
Bukalapak의 /fb_login 엔드포인트는 액세스 토큰을 매개변수로 받고, 자격 증명을 반환해 준다. 그런데 여기서 액세스 토큰의 유효성을 검증하지 않으므로, 다른 웹사이트에서 발급된 토큰을 삽입하여 타 사용자의 권한을 탈취할 수 있게 된다.
Grammarly는 문법, 구두점, 철자 검사 등 다양한 기능을 제공해 주는 AI 기반 글쓰기 도구이다. 이 서비스의 경우 사용자가 작성한 문서를 계정 내에 직접 저장하고 있어, 계정 탈취 시 Victim이 작성한 문서에 액세스 할 수 있게 된다.
Grammarly에서의 OAuth 인증 플로우는 다음과 같다.
OAuth에서는 토큰과 코드라는 두 가지 유형의 데이터타입을 지원해 준다. Vidio와 Bukalapak에서는 토큰을 사용했고 토큰 재사용 공격에 취약했었다.
Grammarly 같이 코드를 사용하는 경우, 서버는 이를 토큰으로 교환해야 하고, 이 때 App ID에 대한 유효성 검사를 수행하게 된다. 그렇다면 어떻게 계정을 탈취할 수 있을까?
Grammarly에서 최종적으로“facebook_login”: {”code”: “코드”} 와 같이 전송되는데, “code” 라는 키워드를 다른 것으로 바꿔보자.
그 중 access_token이 실제로 동작했다고 한다.
TimePlanner에서 획득한 Dan의 토큰을 access_token 값으로 넣고 요청을 보내면 실제로 인증에 성공하는 것을 볼 수 있다. code 값을 쓰고 있어 토큰 재사용 공격에 취약하지 않을 것으로 보였으나, 파라미터 변조를 통해 임의의 토큰을 주입할 수 있었다.
소셜 로그인을 지원하는 경우, 다른 사이트에서 발급 받은 토큰을 재사용할 수 없도록 유효성을 검증해야 한다. Facebook 뿐만 아니라 다양한 서비스에서 이러한 기능을 지원해 주는데, 프로바이더 자체에서 유효성을 검증해줄 순 없기 때문에, 직접 지침에 따라 구현해 주어야 한다.