5

ユーザーが自分の Web サイトで商品を購入できる [ Google でサインイン] ボタンを設定しようとしています。

クライアント側の認証は非常に簡単に見えますが、サーバー側の認証がどのように機能するかを理解するのに苦労しています。サンプル コードでは、クライアント側の「コード」パラメーターをサーバーに渡します。サーバーでは、アクセス トークンと交換できます。アクセス トークンは、ユーザーの友達のリストを表示するために使用できます。

しかし、ユーザーの友達のリストを見たくありません。クライアントが実際に彼らが主張する人物であることを確認したいだけです.

トークンを取得した後、サンプル コードはトークンをセッションに配置し、トークンの存在を使用してユーザーが認証されていることを確認しているようです。それは正しい/安全ですか?購入するときに、サーバーが何らかの方法で (どのように) トークンを再検証する必要がありますか? リクエストごとに Google でトークンを常に再検証する必要がありますか? (うまくいけばそうではありませんか?)

4

1 に答える 1

2

購入を実行する前に、ユーザー ID をクライアントからサーバーに安全に渡し、保存されている資格情報のユーザー ID と照合して、ユーザーが期待どおりのユーザーであることを確認することをお勧めします。これは、攻撃者がセッションをハイジャックしてサイトのユーザーになりすますリプレイ攻撃に対する追加の保護を提供し、ユーザーからの支払いを受け入れる前に行う最も適切なチェックです。

私は、不正行為から保護するためのメカニズムとして、ユーザーの検証だけに頼ることはしません。Google コマース プラットフォームなどの安全な支払いシステムを使用し、コマースのベスト プラクティスに従う必要があります。

注意として、キャッシュされた資格情報が初期化されるたびに、OAuth2 v2 エンドポイントを使用してトークンを確認する必要があります。すでに検証されてサーバー側に保存されているキャッシュされた資格情報を使用する必要があるため、すべての要求をチェックするのは少し過剰に思えます。せいぜい、アクセストークンを更新するときにチェックを実行できますが、リフレッシュトークンを信頼できる場合は、アカウントを作成してそのリフレッシュトークンを設定するときにチェックを実行すれば、十分安全です。

アカウント作成時のユーザー ID 検証に加えて、次の手順が実行されます。

  • クライアントが期待どおりの人物であることを確認します。これにより、クォータを使用して攻撃者に代わって効果的にリクエストを行うために、偽造されたアクセス トークンがアプリに渡されるのを防ぐことができます。
  • アカウントがアプリケーションによって作成されたことを確認します。これにより、ユーザーに代わって追加のアカウントが作成された場合に、クロスサイト リクエスト フォージェリからあなたとユーザーを保護できます。

リンクされた投稿で述べたように、Google+ クイックスタートのサンプル コードは、アカウント認証のためにさまざまなプログラミング言語でこれらのチェックを実行する方法を十分に示しているはずです。

HTML/JS クライアント内で、次のコードは、Google+ userId を確認するために connect メソッドに渡すために userId (特別な文字列「me」ではなく値) を取得する場所を示しています。

  var request = gapi.client.plus.people.get( {'userId' : 'me'} );
  request.execute( function(profile) {
      $('#profile').empty();
      if (profile.error) {
        $('#profile').append(profile.error);
        return;
      }
      helper.connectServer(profile.id);
      $('#profile').append(
          $('<p><img src=\"' + profile.image.url + '\"></p>'));
      $('#profile').append(
          $('<p>Hello ' + profile.displayName + '!<br />Tagline: ' +
          profile.tagline + '<br />About: ' + profile.aboutMe + '</p>'));
      if (profile.cover && profile.coverPhoto) {
        $('#profile').append(
            $('<p><img src=\"' + profile.cover.coverPhoto.url + '\"></p>'));
      }
    });

... 次のコードは、Google+ ID が渡されることを示しています。

connectServer: function(gplusId) {
  console.log(this.authResult.code);
  $.ajax({
    type: 'POST',
    url: window.location.href + '/connect?state={{ STATE }}&gplus_id=' +
        gplusId,
    contentType: 'application/octet-stream; charset=utf-8',
    success: function(result) {
      console.log(result);
      helper.people();
    },
    processData: false,
    data: this.authResult.code
  });
}

Java サンプルでこれらのチェックを実行する関連コードは次のとおりです。

      // Check that the token is valid.
      Oauth2 oauth2 = new Oauth2.Builder(
          TRANSPORT, JSON_FACTORY, credential).build();
      Tokeninfo tokenInfo = oauth2.tokeninfo()
          .setAccessToken(credential.getAccessToken()).execute();
      // If there was an error in the token info, abort.
      if (tokenInfo.containsKey("error")) {
        response.status(401);
        return GSON.toJson(tokenInfo.get("error").toString());
      }
      // Make sure the token we got is for the intended user.
      if (!tokenInfo.getUserId().equals(gPlusId)) {
        response.status(401);
        return GSON.toJson("Token's user ID doesn't match given user ID.");
      }
      // Make sure the token we got is for our app.
      if (!tokenInfo.getIssuedTo().equals(CLIENT_ID)) {
        response.status(401);
        return GSON.toJson("Token's client ID does not match app's.");
      }
      // Store the token in the session for later use.
      request.session().attribute("token", tokenResponse.toString());
      return GSON.toJson("Successfully connected user.");
    } catch (TokenResponseException e) {
      response.status(500);
      return GSON.toJson("Failed to upgrade the authorization code.");
    } catch (IOException e) {
      response.status(500);
      return GSON.toJson("Failed to read token data from Google. " +
          e.getMessage());
    }

サンプルでは、​​ClientID は Google API コンソールから取得されたものであり、アプリケーションごとに異なります。

于 2013-03-15T12:05:08.660 に答える