總覽
我們在 2022 年 2 月 16 日 宣布,將採用更安全的 OAuth 流程,提升 Google OAuth 互動的安全性。本指南可協助您瞭解必要變更和步驟,順利從 OAuth 頻外 (OOB) 流程遷移至支援的替代方案。
這項措施可防範與 Google OAuth 2.0 授權端點互動時,遭到網路釣魚和應用程式冒名攻擊。
什麼是 OOB?
OAuth 頻外 (OOB),也稱為手動複製/貼上選項,是為支援原生用戶端而開發的舊版流程。這類用戶端沒有重新導向 URI,無法在使用者核准 OAuth 同意要求後接受憑證。OOB 流程有遠端網路釣魚風險,用戶端必須遷移至替代方法,才能防範這項安全漏洞。我們將針對所有用戶端類型 (包括網頁應用程式、Android、iOS、通用 Windows 平台 (UWP)、Chrome 應用程式、電視和輸入受限裝置、桌面應用程式) 淘汰 OOB 流程。
重要法規遵循日期
- 2022 年 2 月 28 日 - 封鎖 OOB 流程的新 OAuth 使用情形
- 2022 年 9 月 5 日 - 系統可能會向不符合規定的 OAuth 要求顯示使用者適用的警告訊息
- 2022 年 10 月 3 日:針對 2022 年 2 月 28 日前建立的 OAuth 用戶端,系統將淘汰 OOB 流程
- 2023 年 1 月 31 日 - 封鎖所有現有用戶端 (包括豁免用戶端)
對於不符合規定的要求,系統會向使用者顯示錯誤訊息。 訊息會向使用者說明應用程式遭到封鎖,並顯示您在 Google API 控制台的 OAuth 同意畫面中註冊的支援電子郵件地址。
- 判斷您是否受到影響。
- 如果受到影響,請改用更安全的替代方案。
判斷是否受到影響
這項淘汰措施僅適用於正式版應用程式 (即發布狀態設為「實際運作中」的應用程式)。如果應用程式的測試發布狀態為,這個流程仍可繼續運作。
在 OAuth Branding pageGoogle Cloud Console 中查看發布狀態,如果專案的發布狀態為「實際運作中」,且您使用 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 - 使用「網路檢查器」檢查網路流量
-
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)
如果判定應用程式使用 OOB 流程,且 OAuth 用戶端類型為 Android 或 iOS,請改用建議的 SDK (Android、iOS)。
SDK 可輕鬆存取 Google API,並處理所有對 Google OAuth 2.0 授權端點的呼叫。
請參閱下列說明文件連結,瞭解如何使用建議的 SDK 存取 Google API,而不使用 OOB 重新導向 URI。
在 Android 上存取 Google API
用戶端存取權
以下範例說明如何使用建議的 Google Identity 服務 Android 程式庫,在 Android 裝置上存取用戶端 Google API。
ListrequestedScopes = 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。ListrequestedScopes = 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
),或是搭配
適用於 REST 的 Objective-C 版 Google API 用戶端程式庫,使用擷取器授權者 (GTMFetcherAuthorizationProtocol
)。
請參閱用戶端存取指南,瞭解如何在用戶端存取 Google API。說明如何從用戶端存取 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 應用程式用戶端上使用 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),以接收授權碼。
- 在 Clients page 中設定 重新導向 URI , Google Cloud Console
以下程式碼片段是 NodeJS 範例,說明如何使用 Google Drive API 在伺服器端列出使用者的 Google 雲端硬碟檔案,而不使用 OOB 重新導向 URI。
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。
用戶端存取權
以下 JavaScript 程式碼片段範例,說明如何使用 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
) 流程。