概要
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 日 - 2022 年 3 月 14 日より前に作成された Android、Chrome アプリ、iOS の OAuth クライアントで、ループバック IP アドレス フローがブロックされます。
- 2022 年 10 月 21 日 - 既存のすべてのクライアント(免除されているクライアントを含む)がブロックされます。
ポリシーに準拠していないリクエストには、ユーザー向けのエラー メッセージが表示されます。このメッセージは、アプリがブロックされていることをユーザーに伝えるとともに、Google Cloud コンソールの OAuth 同意画面に登録したサポート メールアドレスを表示します。
移行プロセスを完了するには、主に次の 2 つの手順を行います。
- 影響を受けるかどうかを判断します。
- 影響を受ける場合は、サポートされている別の方法に移行してください。
影響を受けるかどうかを判断する
OAuth アプリケーション タイプを確認する
Google Cloud コンソールの [クライアント ページ] に移動し、[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 拡張機能のページに移動します。
- 拡張機能ページの右上にある [デベロッパー モード] チェックボックスをオンにします。
- モニタリングする拡張機能を選択します。
- 拡張機能ページの [ビューを検証] セクションにある [バックグラウンド ページ] リンクをクリックします。
- [デベロッパー ツール] ダイアログが開き、[ネットワーク] タブでネットワーク トラフィックをモニタリングできます。
- 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)
アプリが Android または iOS の OAuth アプリケーション タイプでループバック IP アドレス フローを使用していると判断した場合は、推奨される SDK(Android、iOS)を使用するように移行する必要があります。
SDK を使用すると、Google API にアクセスして、Google の OAuth 2.0 認可エンドポイントへのすべての呼び出しを処理できます。
次のドキュメント リンクでは、ループバック IP アドレス リダイレクト URI を使用せずに、推奨される SDK を使用して Google API にアクセスする方法について説明します。
Android で Google API にアクセスする
クライアントサイドのアクセス
次の例は、推奨される Google Identity Services 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 <var>ACCESS_TOKEN</var>)か、REST 用 Objective-C 向け Google API クライアント ライブラリでフェッチャー オーソライザ(GTMFetcherAuthorizationProtocol)を使用します。
クライアントサイドで 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 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 ガイドをご覧ください。