루프백 IP 주소 흐름 이전 가이드

개요

2022년 2월 16일, Google은 더 안전한 OAuth 흐름을 사용하여 Google OAuth 상호작용을 더 안전하게 만들 계획을 발표했습니다. 이 가이드에서는 루프백 IP 주소 흐름에서 지원되는 대안으로 성공적으로 이전하는 데 필요한 변경사항과 단계를 설명합니다.

이 노력은 Google의 OAuth 2.0 승인 엔드포인트와 상호작용하는 동안 발생하는 피싱 및 앱 사칭 공격을 방지하기 위한 보호 조치입니다.

루프백 IP 주소 흐름이란 무엇인가요?

루프백 IP 주소 흐름은 사용자가 OAuth 동의 요청을 승인한 후 사용자 인증 정보가 전송되는 리디렉션 URI의 호스트 구성요소로 루프백 IP 주소 또는 localhost를 사용할 수 있도록 지원합니다. 이 흐름은 중간자 공격에 취약하며, 일부 운영체제에서 동일한 루프백 인터페이스에 액세스하는 악성 앱이 승인 서버의 응답을 지정된 리디렉션 URI로 가로채고 승인 코드에 액세스할 수 있습니다.

루프백 IP 주소 흐름은 네이티브 iOS, Android, Chrome OAuth 클라이언트 유형에서 지원 중단되지만 데스크톱 앱에서는 계속 지원됩니다.

주요 규정 준수 날짜

  • 2022년 3월 14일 - 새 OAuth 클라이언트가 루프백 IP 주소 흐름을 사용하지 못하도록 차단됨
  • 2022년 8월 1일 - 규정을 준수하지 않는 OAuth 요청에 사용자 대상 경고 메시지가 표시될 수 있음
  • 2022년 8월 31일 - 루프백 IP 주소 흐름이 차단됨 2022년 3월 14일 이전에 생성된 네이티브 Android, Chrome 앱, iOS OAuth 클라이언트의 경우
  • 2022년 10월 21일 - 모든 기존 클라이언트가 차단됨 (예외 클라이언트 포함)

규정을 준수하지 않는 요청에는 사용자 대상 오류 메시지가 표시됩니다. 이 메시지는 Google API 콘솔의 OAuth 동의 화면에 등록한 지원 이메일을 표시하는 동안 앱이 차단되었음을 사용자에게 전달합니다.

이전 절차를 완료하려면 다음 두 가지 주요 단계를 완료해야 합니다.
  1. 영향을 받는지 확인합니다.
  2. 영향을 받는 경우 지원되는 대안으로 이전합니다.

영향을 받는지 확인

OAuth 클라이언트 ID 유형 검토

Google Cloud 콘솔의 클라이언트 페이지로 이동하여 OAuth 2.0 클라이언트 ID 섹션에서 OAuth 클라이언트 ID 유형을 확인합니다. 다음 중 하나입니다. 웹 애플리케이션, Android, iOS, 유니버설 Windows 플랫폼 (UWP), Chrome 앱, TV 및 제한된 입력 기기, 데스크톱 앱.

클라이언트 유형이 Android, Chrome 앱 또는 iOS이고 루프백 IP 주소 흐름을 사용하는 경우 다음 단계로 진행합니다.

데스크톱 앱 OAuth 클라이언트에서 루프백 IP 주소 흐름을 사용하는 경우 이 지원 중단과 관련된 작업을 할 필요가 없습니다. 해당 OAuth 클라이언트 유형과의 사용은 계속 지원되기 때문입니다.

앱에서 루프백 IP 주소 흐름을 사용하는지 확인하는 방법

앱 코드 또는 나가는 네트워크 호출 (앱에서 OAuth 라이브러리를 사용하는 경우)을 검사하여 앱에서 실행하는 Google OAuth 승인 요청 이 루프백 리디렉션 URI 값을 사용하는지 확인합니다.

애플리케이션 코드 검사

Google OAuth 승인 엔드포인트를 호출하는 애플리케이션 코드 섹션을 검토하고 redirect_uri 매개변수에 다음 값 중 하나가 있는지 확인합니다.
  • redirect_uri=http://127.0.0.1:<port>(예: redirect_uri=http://127.0.0.1:3000)
  • redirect_uri=http://[::1]:<port>(예: redirect_uri=http://[::1]:3000)
  • redirect_uri=http://localhost:<port>(예: redirect_uri=http://localhost:3000)
샘플 루프백 IP 주소 리디렉션 흐름 요청은 다음과 같습니다.
https://accounts.google.com/o/oauth2/v2/auth?
redirect_uri=http://localhost:3000&
response_type=code&
scope=<SCOPES>&
state=<STATE>&
client_id=<CLIENT_ID>

나가는 네트워크 호출 검사

네트워크 호출을 검사하는 방법은 애플리케이션 클라이언트 유형에 따라 다릅니다.
네트워크 호출을 검사하는 동안 Google OAuth 승인 엔드포인트 로 전송된 요청을 찾고 redirect_uri 매개변수에 다음 값 중 하나가 있는지 확인합니다.
  • redirect_uri=http://127.0.0.1:<port>(예: redirect_uri=http://127.0.0.1:3000)
  • redirect_uri=http://[::1]:<port>(예: redirect_uri=http://[::1]:3000)
  • redirect_uri=http://localhost:<port>(예: redirect_uri=http://localhost:3000)
샘플 루프백 IP 주소 리디렉션 흐름 요청은 다음과 같습니다.
https://accounts.google.com/o/oauth2/v2/auth?
redirect_uri=http://localhost:3000&
response_type=code&
scope=<SCOPES>&
state=<STATE>&
client_id=<CLIENT_ID>

지원되는 대안으로 이전

모바일 클라이언트 (Android / iOS)

앱에서 Android 또는 iOS OAuth 클라이언트 유형으로 루프백 IP 주소 흐름을 사용하는 것으로 확인되면 권장 SDK (Android, iOS)를 사용하도록 이전해야 합니다.

SDK를 사용하면 Google API에 쉽게 액세스하고 Google의 OAuth 2.0 승인 엔드포인트에 대한 모든 호출을 처리할 수 있습니다.

아래 문서 링크에서는 루프백 IP 주소 리디렉션 URI를 사용하지 않고 권장 SDK를 사용하여 Google API에 액세스하는 방법을 설명합니다.

Android에서 Google API에 액세스

클라이언트 측 액세스

다음 예에서는 권장되는 Google ID 서비스 Android 라이브러리를 사용하여 Android의 클라이언트 측에서 Google API에 액세스하는 방법을 보여줍니다.

  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    // Access already granted, continue with user action
                    saveToDriveAppFolder(authorizationResult);
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

정의된 메서드에 authorizationResult를 전달하여 콘텐츠를 사용자의 Drive 폴더에 저장합니다. authorizationResult에는 액세스 토큰을 반환하는 getAccessToken() 메서드가 있습니다.

서버 측 (오프라인) 액세스
다음 예에서는 Android의 서버 측에서 Google API에 액세스하는 방법을 보여줍니다.
  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .requestOfflineAccess(webClientId)
            .setRequestedScopes(requestedScopes)
            .build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    String authCode = authorizationResult.getServerAuthCode();
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

authorizationResult에는 액세스 및 갱신 토큰을 가져오기 위해 백엔드로 전송할 수 있는 승인 코드를 반환하는 getServerAuthCode() 메서드가 있습니다.

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) 가져오기 승인자(GTMFetcherAuthorizationProtocol)를 Objective-C for REST용 Google API 클라이언트 라이브러리()와 함께 사용하여 액세스 토큰으로 API를 호출합니다.

클라이언트 측에서 Google 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 앱 클라이언트의 루프백 IP 주소 흐름을 사용하는 것으로 확인되면 Chrome Identity API를 사용하도록 이전해야 합니다.

아래 예에서는 루프백 IP 주소 리디렉션 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 가이드에서 Chrome Identity API로 사용자를 인증하고 Google 엔드포인트를 호출하는 방법을 자세히 알아보세요.