總覽
我們在 2022 年 2 月 16 日宣布,計畫採用更安全的 OAuth 流程,提升 Google OAuth 互動的安全性。本指南可協助您瞭解必要的變更和步驟,順利從回送 IP 位址流程遷移至支援的替代方案。
這項措施旨在防範與 Google OAuth 2.0 授權端點互動時,發生網路釣魚和應用程式冒用攻擊。
什麼是回送 IP 位址流程?
回送 IP 位址流程支援使用回送 IP 位址或 localhost 做為重新導向 URI 的主機元件,使用者核准 OAuth 同意要求後,系統會將憑證傳送至該 URI。這個流程容易受到中間人攻擊,惡意應用程式在某些作業系統上存取相同的回送介面,可能會攔截授權伺服器傳送至指定重新導向 URI 的回應,並取得授權碼。
iOS、Android 和 Chrome OAuth 應用程式類型將淘汰回送 IP 位址流程,但電腦版應用程式仍會繼續支援。
重要法規遵循日期
- 2022 年 3 月 14 日:新的 OAuth 用戶端無法使用回送 IP 位址流程
- 2022 年 8 月 1 日:不符合規定的 OAuth 要求可能會向使用者顯示警告訊息
- 2022 年 8 月 31 日:針對 2022 年 3 月 14 日前建立的 Android、Chrome 應用程式和 iOS OAuth 用戶端,封鎖回送 IP 位址流程
- 2022 年 10 月 21 日:所有現有用戶端都會遭到封鎖 (包括豁免用戶端)
如果要求不符合規定,系統會向使用者顯示錯誤訊息。系統會向使用者顯示訊息,說明應用程式已遭封鎖,並顯示您在 Google Cloud 控制台的 OAuth 同意畫面中註冊的支援電子郵件地址。
遷移程序主要有兩個步驟:
- 判斷您是否受到影響。
- 如果受到影響,請改用支援的替代方法。
判斷您是否受到影響
查看 OAuth 應用程式類型
前往 Google Cloud 控制台的「Clients」(用戶端) 頁面,在「OAuth 2.0 Client IDs」(OAuth 2.0 用戶端 ID) 區段查看 OAuth 應用程式類型。可能為下列任一項:網頁應用程式、Android、iOS、通用 Windows 平台 (UWP)、Chrome 應用程式、電視和輸入裝置受限的裝置、電腦版應用程式。
如果您的應用程式類型是 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>
檢查傳出網路呼叫
檢查網路呼叫的方法會因應用程式類型而異。
- 網頁應用程式:在 Chrome 上檢查網路活動
- Android - 使用網路檢查器檢查網路流量
- Chrome 應用程式
- 前往 Chrome 擴充功能頁面
- 勾選擴充功能頁面右上角的「開發人員模式」核取方塊。
- 選取要監控的擴充功能
- 在擴充功能頁面的「Inspect views」部分,點選「background page」連結
- 系統會開啟「開發人員工具」對話方塊,您可以在「網路」分頁中監控網路流量
- iOS - 使用 Instruments 分析 HTTP 流量
- 電腦版應用程式:使用網路擷取工具,適用於應用程式開發的作業系統
檢查網路呼叫時,請找出傳送至 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)
如果您判斷應用程式使用回送 IP 位址流程,且 OAuth 應用程式類型為 Android 或 iOS,請改用建議的 SDK (Android、iOS)。
SDK 可協助您存取 Google API,並處理對 Google OAuth 2.0 授權端點的所有呼叫。
下列說明文件連結提供相關資訊,說明如何使用建議的 SDK 存取 Google API,而不使用回送 IP 位址重新導向 URI。
在 Android 上存取 Google API
用戶端存取權
以下範例說明如何使用建議的 Google Identity 服務 Android 程式庫,在 Android 用戶端存取 Google API。
List<Scopes> 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 傳遞至定義的方法,將內容儲存至使用者的雲端硬碟資料夾。authorizationResult 具有會傳回存取權杖的 getAccessToken() 方法。
伺服器端 (離線) 存取權
以下範例說明如何在 Android 伺服器端存取 Google API。
List<Scopes> 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() }
如要使用存取權杖呼叫 API,請在 REST 或 gRPC 要求的標頭中加入存取權杖 (Authorization: Bearer ACCESS_TOKEN),或搭配適用於 Objective-C for REST 的 Google API 用戶端程式庫使用擷取器授權工具 (GTMFetcherAuthorizationProtocol)。
請參閱用戶端存取指南,瞭解如何在用戶端存取 Google API。
伺服器端 (離線) 存取權
以下範例說明如何在伺服器端存取 Google API,以支援 iOS 用戶端。
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 following example 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 指南。