개요
2022년 2월 16일, Google에서는 더 안전한 OAuth 흐름을 사용하여 Google OAuth 상호작용을 더욱 안전하게 만들 계획을 발표했습니다. 이 가이드에서는 OAuth 대역 (OOB) 흐름에서 지원되는 대안으로 성공적으로 이전하는 데 필요한 변경사항과 단계를 알아봅니다.
이는 Google의 OAuth 2.0 승인 엔드포인트와 상호작용하는 동안 피싱 및 앱 명의 도용 공격을 차단하기 위한 조치입니다.
OOB이란 무엇인가요?
OAuth 대역 외(OOB)(수동 복사/붙여넣기 옵션이라고도 함)은 사용자가 OAuth 동의 요청을 승인한 후 사용자 인증 정보를 허용하도록 리디렉션 URI가 없는 네이티브 클라이언트를 지원하도록 개발된 기존 흐름입니다. OOB 흐름은 원격 피싱의 위험을 초래하므로 클라이언트는 이 취약점으로부터 보호하기 위해 다른 방법으로 이전해야 합니다.웹 클라이언트, Android, iOS, 범용 Windows 플랫폼 (UWP), Chrome 앱, TV 및 제한된 입력 기기, 데스크톱 앱 등 모든 클라이언트 유형에 관해 OOB 흐름이 지원 중단됩니다.
주요 규정 준수 날짜
- 2022년 2월 28일: OOB 흐름에서 새로운 OAuth 사용이 차단됨
- 2022년 9월 5일 - 정책을 준수하지 않는 OAuth 요청에 사용자 대상 경고 메시지가 표시될 수 있습니다.
- 2022년 10월 3일 - 2022년 2월 28일 이전에 생성된 OAuth 클라이언트의 OOB 흐름이 지원 중단되었습니다.
- 2023년 1월 31일 - 모든 기존 클라이언트 차단됨(제외된 클라이언트 포함)
정책을 준수하지 않는 요청의 경우 사용자에게 표시되는 오류 메시지가 표시됩니다. 이 메시지는 Google API 콘솔의 OAuth 동의 화면에 등록한 지원 이메일을 표시하면서 앱이 차단되었음을 사용자에게 전달합니다.
- 영향을 받는지 확인합니다.
- 영향을 받는 경우 더 안전한 대안으로 이전하세요.
영향을 받는지 확인
이 지원 중단은 프로덕션 앱 (게시 상태가 프로덕션 중으로 설정된 앱)에만 적용됩니다. 이 흐름은 게시 상태 테스트인 앱에서 계속 작동합니다.
Google API Console 의 OAuth Consent Screen page에서 게시 상태를 검토한 후 게시 상태가 '프로덕션 단계'인 프로젝트에서 OOB 흐름을 사용하고 있다면 다음 단계로 진행합니다.
앱이 OOB 흐름을 사용하는지 확인하는 방법
앱 코드 또는 발신 네트워크 호출 (앱에서 OAuth 라이브러리를 사용하는 경우)을 확인하여 앱에서 Google OAuth 승인 요청이 OOB 리디렉션 URI 값을 사용하고 있는지 확인하세요.
애플리케이션 코드 검사
redirect_uri
매개변수에 다음 값 중 하나가 포함되어 있는지 확인합니다.
redirect_uri=urn:ietf:wg:oauth:2.0:oob
redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
redirect_uri=oob
https://accounts.google.com/o/oauth2/v2/auth? response_type=code& scope=<SCOPES>& state=<STATE>& redirect_uri=urn:ietf:wg:oauth:2.0:oob& client_id=<CLIENT_ID>
발신 네트워크 통화 검사
- 웹 애플리케이션 - Chrome에서 네트워크 활동 검사
- Android - Network Inspector로 네트워크 트래픽 검사
-
Chrome 앱
- Chrome 확장 프로그램 페이지로 이동합니다.
- 확장 프로그램 페이지 오른쪽 상단에 있는 개발자 모드 체크박스를 선택합니다.
- 모니터링할 확장 프로그램을 선택합니다.
- 확장 프로그램 페이지의 뷰 검사 섹션에서 백그라운드 페이지 링크를 클릭합니다.
- 네트워크 탭에서 네트워크 트래픽을 모니터링할 수 있는 개발자 도구 팝업이 열립니다.
- iOS - Instruments로 HTTP 트래픽 분석
- 범용 Windows 플랫폼 (UWP) - Visual Studio에서 네트워크 트래픽 검사
- 데스크톱 앱 - 앱이 개발된 운영체제에서 사용할 수 있는 네트워크 캡처 도구를 사용합니다.
redirect_uri
매개변수에 다음 값이 포함되어 있는지 확인합니다.redirect_uri=urn:ietf:wg:oauth:2.0:oob
redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
redirect_uri=oob
https://accounts.google.com/o/oauth2/v2/auth? response_type=code& scope=<SCOPES>& state=<STATE>& redirect_uri=urn:ietf:wg:oauth:2.0:oob& client_id=<CLIENT_ID>
안전한 대안으로 이전
모바일 클라이언트 (Android / iOS)
앱이 Android 또는 iOS OAuth 클라이언트 유형에서 OOB 흐름을 사용하고 있다면 Google 로그인 모바일 SDK(Android, iOS)를 사용하도록 이전해야 합니다.
이 SDK를 사용하면 Google API에 쉽게 액세스하고 Google의 OAuth 2.0 승인 엔드포인트에 대한 모든 호출을 처리할 수 있습니다.
아래 문서 링크에서는 OOB 리디렉션 URI를 사용하지 않고 Google 로그인 SDK를 사용하여 Google API에 액세스하는 방법을 설명합니다.
Android에서 Google API에 액세스
서버 측 (오프라인) 액세스
아래 예는 Android의 서버 측에서 Google API에 액세스하는 방법을 보여줍니다.Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data); try { GoogleSignInAccount account = task.getResult(ApiException.class); // request a one-time authorization code that your server exchanges for an // access token and sometimes refresh token String authCode = account.getServerAuthCode(); // Show signed-in UI updateUI(account); // TODO(developer): send code to server and exchange for access/refresh/ID tokens } catch (ApiException e) { Log.w(TAG, "Sign-in failed", e); updateUI(null); }
서버 측에서 Google API에 액세스하는 방법은 서버 측 액세스 가이드를 참조하세요.
iOS 앱에서 Google API에 액세스
클라이언트 측 액세스
아래 예는 iOS의 클라이언트 측에서 Google API에 액세스하는 방법을 보여줍니다.
user.authentication.do { authentication, error in guard error == nil else { return } guard let authentication = authentication else { return } // Get the access token to attach it to a REST or gRPC request. let accessToken = authentication.accessToken // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for // use with GTMAppAuth and the Google APIs client library. let authorizer = authentication.fetcherAuthorizer() }
REST 또는 gRPC 요청 헤더에 액세스 토큰을 포함하거나(Authorization: Bearer ACCESS_TOKEN
) REST용 Objective-C용 Google API 클라이언트 라이브러리(
)와 함께 가져오기 도구(GTMFetcherAuthorizationProtocol
)를 사용하여 액세스 토큰을 사용하여 API를 호출합니다.
클라이언트 측에서 Google API에 액세스하는 방법은 클라이언트 측 액세스 가이드를 참고하세요. 를 참조하세요.
서버 측 (오프라인) 액세스
아래 예는 iOS 클라이언트를 지원하기 위해 서버 측에서 Google API에 액세스하는 방법을 보여줍니다.GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in guard error == nil else { return } guard let user = user else { return } // request a one-time authorization code that your server exchanges for // an access token and refresh token let authCode = user.serverAuthCode }
서버 측에서 Google API에 액세스하는 방법은 서버 측 액세스 가이드를 참조하세요.
Chrome 앱 클라이언트
앱이 Chrome 앱 클라이언트에서 OOB 흐름을 사용하는 것으로 확인되면 Chrome Identity API를 사용하도록 이전해야 합니다.
아래 예는 OOB 리디렉션 URI를 사용하지 않고 모든 사용자 연락처를 가져오는 방법을 보여줍니다.
window.onload = function() { document.querySelector('button').addEventListener('click', function() { // retrieve access token chrome.identity.getAuthToken({interactive: true}, function(token) { // .......... // the example below shows how to use a retrieved access token with an appropriate scope // to call the Google People API contactGroups.get endpoint fetch( 'https://people.googleapis.com/v1/contactGroups/all?maxMembers=20&key=API_KEY', init) .then((response) => response.json()) .then(function(data) { console.log(data) }); }); }); };
인증된 사용자에게 액세스하고 Chrome Identity API로 Google 엔드포인트를 호출하는 방법에 관한 자세한 내용은 Chrome Identity API 가이드를 참고하세요.
웹 애플리케이션
앱이 웹 애플리케이션에 OOB 흐름을 사용하는 것으로 확인되면 Google API 클라이언트 라이브러리 중 하나를 사용하도록 이전해야 합니다. 다양한 프로그래밍 언어용 클라이언트 라이브러리는 여기에서 확인할 수 있습니다.
라이브러리를 사용하면 Google API에 쉽게 액세스하고 Google 엔드포인트에 대한 모든 호출을 처리할 수 있습니다.
서버 측 (오프라인) 액세스
- 서버를 세우고 공개적으로 액세스할 수 있는 엔드포인트 (리디렉션 URI)를 정의하여 승인 코드를 받습니다.
- Google API Console의 Credentials page 리디렉션 URI 를 구성합니다.
아래 코드 스니펫은 OOB 리디렉션 URI를 사용하지 않고 Google Drive API를 사용하여 서버 측에서 사용자의 Google Drive 파일을 나열하는 NodeJS 예를 보여줍니다.
async function main() { const server = http.createServer(async function (req, res) { if (req.url.startsWith('/oauth2callback')) { let q = url.parse(req.url, true).query; if (q.error) { console.log('Error:' + q.error); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { // TODO(developer): Handle response / error. }); } } }
서버 측에서 Google API에 액세스하는 방법은 서버 측 웹 앱 가이드를 참조하세요.
클라이언트 측 액세스
자바스크립트의 아래 코드 스니펫은 Google API를 사용하여 클라이언트 측에서 사용자의 캘린더 일정에 액세스하는 예를 보여줍니다.
// initTokenClient() initializes a new token client with your // web app's client ID and the scope you need access to const client = google.accounts.oauth2.initTokenClient({ client_id: 'YOUR_GOOGLE_CLIENT_ID', scope: 'https://www.googleapis.com/auth/calendar.readonly', // callback function to handle the token response callback: (tokenResponse) => { if (tokenResponse && tokenResponse.access_token) { gapi.client.setApiKey('YOUR_API_KEY'); gapi.client.load('calendar', 'v3', listUpcomingEvents); } }, }); function listUpcomingEvents() { gapi.client.calendar.events.list(...); }
클라이언트 측에서 Google API에 액세스하는 방법은 클라이언트 측 웹 앱 가이드를 참조하세요.
데스크톱 클라이언트
앱이 데스크톱 클라이언트에서 OOB 흐름을 사용하는 것으로 확인되면
루프백 IP 주소 (localhost
또는 127.0.0.1
) 흐름을 사용하도록 마이그레이션해야 합니다.