Identity-Aware Proxy によるユーザー認証

ウェブアプリでユーザーを認証する必要が生じることがよくあります。また、通常、アプリで特別なプログラミングが必要になります。Google Cloud Platform アプリについては、こうした役割を Identity-Aware Proxy サービスに任せることができます。選択したユーザーのみにアクセスを制限する場合は、アプリケーションを変更する必要はありません。アプリケーションがユーザー ID を認識する必要がある場合(ユーザー設定をサーバー側で保持する場合など)、Identity-Aware Proxy では最小限のアプリケーション コードを使用してそれを実現します。

Identity-Aware Proxy とは

Identity-Aware Proxy(IAP)は Google Cloud Platform のサービスです。アプリケーションに送信されたウェブ リクエストをインターセプトし、リクエストを送信したユーザーを Google の ID サービスを使用して認証し、認証されたユーザーからのリクエストのみを通過させます。さらに、リクエスト ヘッダーを変更して認証されたユーザーに関する情報を含めることができます。

この Codelab では、独自のアプリケーションを作成してアクセスを制限し、IAP からユーザー ID を取得する手順について説明します。

作業内容

この Codelab では、Google App Engine で最小限のウェブ アプリケーションを構築してから、Identity-Aware Proxy を使用してアプリケーションへのアクセスを制限し、ユーザー ID 情報を提供するさまざまな方法を確認します。作成するアプリの機能は次のとおりです。

  • ウェルカム ページを表示する
  • IAP が提供したユーザー ID 情報にアクセスする
  • 暗号化検証を使用してユーザー ID 情報のなりすましを防ぐ

ラボの内容

  • Python 3.7 を使用してシンプルな App Engine アプリを作成してデプロイする方法
  • アプリへのアクセスを制限する IAP を有効または無効にする
  • ユーザー ID 情報を IAP からアプリに取り込む
  • なりすましを防ぐために IAP からの情報を暗号的に検証する

必要なもの

  • 最新のウェブブラウザ(Chrome など)
  • Python プログラミング言語の基本的な知識

この Codelab では、Google App Engine と IAP に焦点を当てます。関連のない概念やコードブロックについては詳しく触れず、コードはコピーして貼るだけの状態で提供されています。

Cloud Shell コマンドライン環境で作業します。まず、その環境を開き、サンプルコードを取得します。

Console と Cloud Shell を起動する

ラボページの左上にある [Google Console を開く] ボタンをクリックします。このボタンの下に表示されているユーザー名とパスワードを使用してログインする必要があります。

この Codelab のすべてのコマンドは、自動的に作成され、開かれたプロジェクトの Cloud Shell 内で実行されます。コンソール ページのヘッダーの右側にある [Cloud Shell をアクティブにする] アイコンをクリックして Cloud Shell を開きます。ページの下半分では、コマンドを入力して実行できます。

コマンドは自分の PC から実行できますが、最初に必要な開発ソフトウェアをインストールして構成する必要があります。Cloud Shell には必要なソフトウェア ツールがすべて揃っています。

コードをダウンロードする

コマンドを入力できるように Cloud Shell のコマンドラインの領域をクリックします。GitHub からコードを取得して、コードフォルダに移動します。

git clone https://github.com/googlecodelabs/user-authentication-with-iap.git
cd iap-codelab

このフォルダには、この Codelab のステップごとに 1 つのサブフォルダが存在します。ステップごとに適切なフォルダに変更します。

Python 3.7 で作成された「Hello, World」のスタートページを表示する App Engine スタンダード アプリケーション。デプロイしてテストした後、IAP を使用してアクセスを制限します。

アプリケーション コードを確認する

メインのプロジェクト フォルダから、この手順のコードを含む 1-HelloWorld サブフォルダに変更します。

cd 1-HelloWorld

アプリケーション コードは main.py ファイルにあります。Flask ウェブ フレームワークを使用して、ウェブ リクエストにテンプレートの内容で対応します。そのテンプレート ファイルは templates/index.html にあり、このステップではプレーン HTML のみが含まれています。2 つ目のテンプレート ファイルには、templates/privacy.html のプライバシー ポリシーの骨格例が含まれています。

他に 2 つのファイルがあります。requirements.txt は、アプリケーションが使用するデフォルト以外の Python ライブラリをすべて一覧表示します。app.yaml は、Python 3.7 App Engine アプリケーションであることを Google Cloud Platform に通知します。

次のような cat コマンドを使用してシェル内の各ファイルを一覧表示できます。

cat main.py

または、Cloud Shell ウィンドウの右上にある鉛筆アイコンをクリックして Cloud Shell コードエディタを開き、その方法でコードを検証することもできます。

このステップでは、ファイルを変更する必要はありません。

App Engine にデプロイする

Python 3.7 用 App Engine スタンダード環境にアプリをデプロイします。

gcloud app deploy

デプロイ先のリージョンを選択するように求められる場合があります。近くの「標準サポート」と表示されたものを選択してください。続行するかどうかを確認するメッセージが表示されたら、「Y」と入力します。

数分でデプロイが完了し、gcloud app browse を使用してアプリケーションを表示できるというメッセージが表示されます。コマンドを入力します。ブラウザで新しいタブが開かない場合は、表示されているリンクをクリックして新しいタブを開くか、必要に応じて手動で新しいタブを開きます。このアプリを実行するのは今回が初めてなので、クラウド インスタンスが起動するのに数秒かかり、それから以下のウィンドウが表示されます。

インターネットに接続されていれば、どのパソコンからも同じ URL でそのウェブページにアクセスできます。アクセスはまだ制限されていません。

IAP でアクセスを制限する

Cloud Console ウィンドウで、左上にあるメニュー アイコンをクリックし、[セキュリティ]、[Identity-Aware Proxy] の順にクリックします。

このプロジェクトの認証オプションを初めて有効にしたため、IAP を使用するには OAuth 同意画面を設定する必要があるというメッセージが表示されます。

[同意画面を構成] ボタンをクリックします。新しいタブが開き、同意画面が表示されます。

下記の必須項目に適切な値を入力してください。

アプリケーション名

IAP の例

サポートメール

メールアドレスがあらかじめ入力されている場合があります。

承認済みドメイン

アプリケーションの URL のホスト名の部分(例: iap-example-999999.appspot.com)。これは、先ほど開いた Hello World ウェブページのアドレスバーで確認できます。その URL から先頭の https:// や末尾の / は含めないでください。

この値を入力したら、Enter キーを押します。

アプリケーション ホームページ リンク

アプリの表示に使用した URL

アプリケーション プライバシー ポリシー リンク

アプリのプライバシー ページのリンク(末尾に「/privacy」を追加したホームページのリンクと同じ)

[保存] をクリックします。認証情報の作成を求めるメッセージが表示されます。この Codelab 用に認証情報を作成する必要はないため、このブラウザタブを閉じるだけです。

[Identity-Aware Proxy] に戻ってページを更新します。保護できるリソースの一覧が表示されます。

App Engine アプリの行で [IAP] 列の切り替えボタンをクリックして、IAP を有効にします。

IAP で保護されるドメイン名が表示されます。[オンにする] をクリックします。

ブラウザタブを開いてアプリの URL に移動します。「Google でログイン」画面が表示され、アプリにログインするよう求められます。

Google アカウントまたは G Suite アカウントでログインします。アクセス拒否の画面が表示されます。

アプリは IAP で正常に保護されていますが、どのアカウントを通過させるのかを IAP にまだ指示していません。

コンソールの [Identity-Aware Proxy] ページに戻り、[App Engine] アプリの隣にあるチェックボックスをオンにすると、ページの右側にサイドバーが表示されます。

アクセスを許可する各メールアドレス(または Google グループ アドレス、または G Suite ドメイン名)をメンバーとして追加する必要があります。[メンバーを追加] をクリックします。メールアドレスを入力し、Cloud IAP または IAP で保護されたウェブアプリ ユーザーのロールを選択して、そのアドレスに割り当てます。同じ方法で追加のアドレスや G Suite ドメインを入力できます。

[保存] をクリックします。「ポリシーを更新しました」というメッセージがウィンドウの下部に表示されます。

アプリに戻ってページを再読み込みします。承認されたユーザーでログイン済みのため、ウェブアプリが表示されます。ただし、「IAP による承認の再確認を行わないため、アクセスできません」というページが表示されることがあります。その場合は次のステップに従います。

  • ウェブブラウザで、https://iap-example-999999.appspot.com/_gcp_iap/clear_login_cookie のように URL の末尾に /_gcp_iap/clear_login_cookie を追加したホームページ アドレスを開きます。
  • アカウントがすでに表示された状態で、新たに「Google でログイン」画面が表示されます。アカウントをクリックせずに、[別のアカウントを使用] をクリックして認証情報を再入力ます。
  • 以下の手順を行うと、IAP によるアクセスの再確認が行われ、アプリのホーム画面が表示されます。

別の有効な Gmail アカウントや G Suite アカウントをお持ちのうえで、別のブラウザを利用できる場合、またはブラウザでシークレット モードを使用できる場合は、そのブラウザを使ってアプリケーションのページに移動し、他のアカウントでログインしてみてください。アカウントは承認されていないため、アプリが代わりに「アクセス権がありません」の画面が表示されます。

アプリが IAP で保護されると、通過するウェブ リクエスト ヘッダーで IAP により提供される ID 情報を使用できるようになります。このステップでアプリケーションが取得するのは、ログイン ユーザーのメールアドレスと、Google ID サービスによってそのユーザーに割り当てられた永続的な一意のユーザー ID です。そのデータはウェルカム ページでユーザーに表示されます。

これはステップ 2 です。最後のステップは、Cloud Shell を iap-codelab/1-HelloWorld フォルダで開いたままにしたものです。次のステップのフォルダに移動します。

cd ~/iap-codelab/2-HelloUser

App Engine にデプロイする

デプロイには数分かかります。まず、Python 3.7 の App Engine スタンダード環境にアプリをデプロイします。

gcloud app deploy

続行するかどうかを確認するメッセージが表示されたら、「Y」と入力します。デプロイは数分で完了します。待っている間に、以下の説明に沿ってアプリケーション ファイルを調べることができます。

デプロイの準備が整うと、gcloud app browse を使用してアプリケーションを表示できるというメッセージが表示されます。コマンドを入力します。ブラウザで新しいタブが開かない場合は表示されているリンクをコピーしますが、通常は新しいタブで開きます。次のようなページが表示されます。

アプリケーションが旧バージョンから新しいバージョンに置き換わるまで、数分かかる場合があります。必要に応じてページを更新して上記のようなページを表示します。

アプリケーション ファイルを調べる

このフォルダにはステップ 1 と同じファイル セットが含まれていますが、2 つのファイル main.pytemplates/index.html が変更されています。プログラムは、IAP によってリクエスト ヘッダーに提供されるユーザー情報を取得するように変更されました。また、テンプレートにそのデータが表示されるようになりました。

main.py には、IAP 提供の ID データを取得する行が 2 つあります。

user_email = request.headers.get('X-Goog-Authenticated-User-Email')
user_id = request.headers.get('X-Goog-Authenticated-User-ID')

X-Goog-Authenticated-User- ヘッダーは IAP によって提供されます。名前の大文字と小文字は区別されないため、必要に応じてすべて小文字または大文字で指定できます。render_template ステートメントにこれらの値が含まれるようになったので、表示が可能です。

page = render_template('index.html', email=user_email, id=user_id)

index.html テンプレートでこれらの値を表示するには、二重の中かっこで名前を囲みます。

Hello, {{ email }}! Your persistent ID is {{ id }}.

ご覧のように、データの先頭には「accounts.google.com:」が付いており、情報の提供元を示します。必要な場合、アプリケーションではコロンまでのすべてを削除して未加工の値を取得できます。

IAP を無効にする

IAP が無効になっている場合、または、なんらかの理由で(同じクラウド プロジェクトで実行されている他のアプリケーションなどによって)バイパスされている場合にこのアプリがどうなるのかを確認するために IAP を無効にします。

Cloud Console ウィンドウで、左上にあるメニュー アイコンをクリックし、[セキュリティ]、[Identity-Aware Proxy] の順にクリックします。App Engine アプリの横にある IAP の切り替えスイッチをクリックして IAP を無効にします。

すべてのユーザーがアプリにアクセスできるようになることを示す警告が示されます。

アプリケーションのウェブページを更新します。同じページが表示されますが、ユーザー情報は表示されなくなります。

アプリケーションは保護されなくなったため、ユーザーは IAP を経由したように見えるウェブ リクエストを送信できました。たとえば、Cloud Shell で次の curl コマンドを実行して実行します(<your-url-here> をアプリの正しい URL に置き換えます)。

curl -X GET <your-url-here> -H "X-Goog-Authenticated-User-Email: totally fake email"

ウェブページは、次のようなコマンドラインに表示されます。

<!doctype html>
<html>
<head>
  <title>IAP Hello User</title>
</head>
<body>
  <h1>Hello World</h1>

  <p>
    Hello, totally fake email! Your persistent ID is None.
  </p>

  <p>
    This is step 2 of the <em>User Authentication with IAP</em>
    codelab.
 </p>

</body>
</html>

IAP が無効になっていたりバイパスされていたりすることをアプリケーションが認識する手段はありません。潜在的なリスクがある場合は、ステップ 3 に解決策を示します。

IAP がオフであったりバイパスされたりするリスクがある場合、アプリでは受信した ID 情報が有効であることを確認できます。これは、IAP によって追加された 3 つ目のウェブ リクエスト ヘッダー(X-Goog-IAP-JWT-Assertion)を使用します。ヘッダーの値は暗号で署名されたオブジェクトで、ユーザー ID データも含まれています。アプリケーションはそのデジタル署名を検証し、そのオブジェクトの中で提供されたデータを使用します。そのデータは IAP から改変されずに提供されたものです。

デジタル署名の検証には、最新の Google 公開鍵セットの取得などの追加のステップがいくつか必要です。アプリケーションでこれらの追加のステップが必要かどうかは、他のユーザーが IAP を無効にしたりバイパスしたりできる可能性があるか、またはアプリケーションの機密性などに基づいて決めることができます。

これはステップ 3 で、最後のステップは Cloud Shell を iap-codelab/2-HelloUser フォルダで開いた状態で終了します。次のステップのフォルダに移動します。

cd ~/iap-codelab/3-HelloVerifiedUser

App Engine にデプロイする

Python 3.7 用の App Engine スタンダード環境にアプリをデプロイします。

gcloud app deploy

続行するかどうかを確認するメッセージが表示されたら、「Y」と入力します。デプロイは数分で完了します。待っている間に、以下の説明に沿ってアプリケーション ファイルを調べることができます。

デプロイの準備が整うと、gcloud app browse を使用してアプリケーションを表示できるというメッセージが表示されます。コマンドを入力します。ブラウザで新しいタブが開かない場合は、表示されているリンクをコピーして貼り付けると、通常は新しいタブで開きます。

ステップ 2 で IAP を無効にしたため、アプリケーションに IAP データは提供されないことに注意してください。次のようなページが表示されます。

前のタスクと同様、最新バージョンが有効になって新しいページが表示されるまでに、場合によっては数分待つ必要があります。

IAP が無効になっているため、参照できるユーザー情報はありません。ここで IAP を有効に戻します。

Cloud Console ウィンドウで、左上にあるメニュー アイコンをクリックし、[セキュリティ]、[Identity-Aware Proxy] の順にクリックします。App Engine アプリの横にある IAP の切り替えスイッチをクリックして IAP を再度オンにします。

ページを更新すると、次のようになります。

確認済みメソッドで指定されたメールアドレスには、accounts.google.com: プレフィックスが付いていません。

IAP がオフになっているかバイパスされている場合、検証されたデータは有効な署名を使用できないため紛失または無効になります。ただし、Google の秘密鍵の所有者がそのデータを作成した場合を除きます。

アプリケーション ファイルを調べる

このフォルダにはステップ 2 と同じファイル セットが含まれ、2 つのファイルが変更され、1 つの新しいファイルがあります。新しいファイルは auth.py であり、暗号署名された ID 情報を取得して検証するための user() メソッドを提供します。変更されたファイルは main.pytemplates/index.html になり、そのメソッドの結果が使用されるようになります。ステップ 2 で確認された未検証のヘッダーも比較のために表示されています。

新しい機能は主に user() 関数に組み込まれています。

def user():
    assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
    if assertion is None:
        return None, None

    info = jwt.decode(
        assertion,
        keys(),
        algorithms=['ES256'],
        audience=audience()
    )

    return info['email'], info['sub']

assertion は、指定されたリクエスト ヘッダーで提供される暗号署名付きデータです。コードはライブラリを使用して、このデータを検証およびデコードします。検証では、署名されたデータを確認し、そのデータが(基本的には保護されている Google Cloud プロジェクト用に)準備されていることを対象に知らせるために Google 提供の公開鍵を使用します。ヘルパー関数 keys()audience() はこれらの値を収集して返します。

署名されたオブジェクトには、確認済みのメールアドレスと一意の ID 値(登録者は sub の標準フィールド)の 2 つのデータが必要です。

これでステップ 3 は完了です。

App Engine ウェブ アプリケーションをデプロイしました。ステップ 1 では、アプリケーションへのアクセスを選択したユーザーのみに制限しました。ステップ 2 では、IAP がアプリケーションへのアクセスを許可したユーザーの ID を取得して表示し、IAP が無効化またはバイパスされた場合に、なりすましの可能性を確認しました。ステップ 3 では、なりすましができないユーザー ID の暗号署名アサーションを確認しました。

この Codelab で使用した Google Cloud Platform リソースは App Engine インスタンスのみです。アプリをデプロイするたびに、新しいバージョンが作成され、削除されるまで存在します。プロジェクトとそのプロジェクト内のすべてのリソースを削除するには、ラボを終了します。