このガイドについて
Chrome Verified Access API を使用すると、VPN、イントラネットなどのネットワーク サービス、 クライアントが真正かつ正規のものであることを確認し、 準拠していることを確認する必要があります。ほとんどの大企業では 企業の管理対象デバイスのみ WPA2 EAP-TLS ネットワークに接続できるようにする より上位のアクセス、相互 TLS のイントラネット ページなどです。多数の既存 同じクライアントに対するヒューリスティック チェックに依存している できます。ここで問題となるのは デバイス自体が正当なステータスであることを 改ざんできます。このガイドでは、ハードウェア格納型暗号の保証について説明します。 デバイスの ID 情報であること、その状態が変更されておらず、ポリシーに準拠していること 。確認済みアクセスと呼ばれるものです
主な対象者 | 企業の IT ドメイン管理者 |
技術コンポーネント | ChromeOS、Google Verified Access API |
確認済みアクセスの前提条件
確認済みアクセスのプロセスを実装する前に、次の設定を完了してください。
API を有効にする
Google API Console プロジェクトをセットアップし、API を有効にします。
- Google Cloud コンソールで Google API Console。
- [ 有効な API とサービスのページをご覧ください。
- Chrome Verified Access API を有効にします。
- Google Cloud API ドキュメントに沿って、アプリケーションの API キーを作成します。
サービス アカウントを作成する
ネットワーク サービスが Chrome Verified Access API にアクセスして チャレンジ レスポンス、 サービス アカウントとサービス アカウント キーを作成する (新しい Cloud プロジェクトを作成する必要はなく、同じプロジェクトを使用できます)。
サービス アカウント キーを作成すると、サービス アカウントが作成されます。 ダウンロードされました。これは秘密鍵の唯一のコピーなので、 安全な場所に保管してください。
管理対象の Chromebook デバイスを登録する
Chrome で適切に管理された Chromebook デバイスが必要です 確認済みアクセスの拡張機能です
- Chromebook デバイスを企業または教育機関向けの管理対象として登録しておく必要があります。
- デバイスのユーザーは、同じドメインの登録済みユーザーである必要があります。
- 確認済みアクセスの Chrome 拡張機能をデバイスにインストールする必要があります。
- 確認済みアクセスを有効にするようにポリシーが設定されているため、Chrome を許可リストに登録 そのサービス アカウントに対する API へのアクセス権を付与し、 (詳細は「 Google 管理コンソールのヘルプ ドキュメントをご覧ください)。
ユーザーとデバイスを確認する
デベロッパーは、確認済みアクセスをユーザー確認またはデバイス確認に使用することも、両方を使用することも可能 セキュリティを強化します。
デバイスの確認 - 問題がない場合、デバイスの確認では Chrome デバイスが管理対象ドメインに登録され、 ドメインで指定された、確認付きブートモードのデバイス ポリシーに準拠しています なります。デバイスを参照する権限がネットワーク サービスに付与されているかどうか (Google 管理コンソールのヘルプ ドキュメントを参照)に認証情報を入力すると、 監査、トラッキング、Directory API の呼び出しに使用できるデバイス ID。
ユーザー確認 - 確認できた場合、ユーザー確認によって保証が行われます。 ログインしている Chrome ユーザーが管理対象ユーザーであり、登録済みのデバイスを使用している ドメインで指定された、確認付きブートモードのユーザー ポリシーに準拠している なります。ネットワーク サービスに受信権限が 追加のユーザーデータがあると、この API は発行された証明書署名リクエストも (Signed-public-key-and-challenge(SPKAC)という形式の CSR、 (keygen 形式とも呼ばれます)。
ユーザーとデバイスを確認する方法
確認を行う - デバイスの Chrome 拡張機能が確認済み API にアクセスしてチャレンジを取得してください。データの不透明さと 構造(Google によって署名された blob)で、保存時間は 1 分です。つまり、 古いチャレンジが使用されると、チャレンジ / レスポンス検証(ステップ 3)は失敗します。
最も単純なユースケースでは、ユーザーがボタンをクリックしてこのフローを開始します。 (これは Google 提供のサンプル拡張機能でもあります) 推奨されません)。
var apiKey = 'YOUR_API_KEY_HERE'; var challengeUrlString = 'https://verifiedaccess.googleapis.com/v2/challenge:generate?key=' + apiKey; // Request challenge from URL var xmlhttp = new XMLHttpRequest(); xmlhttp.open('POST', challengeUrlString, true); xmlhttp.send(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { var challenge = xmlhttp.responseText; console.log('challenge: ' + challenge); // v2 of the API returns an encoded challenge so no further challenge processing is needed } };
チャレンジをエンコードするヘルパーコード - API v1 を使用している場合、 チャレンジをエンコードする必要があります。
// This can be replaced by using a third-party library such as // [https://github.com/dcodeIO/ProtoBuf.js/wiki](https://github.com/dcodeIO/ProtoBuf.js/wiki) /** * encodeChallenge convert JSON challenge into base64 encoded byte array * @param {string} challenge JSON encoded challenge protobuf * @return {string} base64 encoded challenge protobuf */ var encodeChallenge = function(challenge) { var jsonChallenge = JSON.parse(challenge); var challengeData = jsonChallenge.challenge.data; var challengeSignature = jsonChallenge.challenge.signature; var protobufBinary = protoEncodeChallenge( window.atob(challengeData), window.atob(challengeSignature)); return window.btoa(protobufBinary); }; /** * protoEncodeChallenge produce binary encoding of the challenge protobuf * @param {string} dataBinary binary data field * @param {string} signatureBinary binary signature field * @return {string} binary encoded challenge protobuf */ var protoEncodeChallenge = function(dataBinary, signatureBinary) { var protoEncoded = ''; // See https://developers.google.com/protocol-buffers/docs/encoding // for encoding details. // 0x0A (00001 010, field number 1, wire type 2 [length-delimited]) protoEncoded += '\u000A'; // encode length of the data protoEncoded += varintEncode(dataBinary.length); // add data protoEncoded += dataBinary; // 0x12 (00010 010, field number 2, wire type 2 [length-delimited]) protoEncoded += '\u0012'; // encode length of the signature protoEncoded += varintEncode(signatureBinary.length); // add signature protoEncoded += signatureBinary; return protoEncoded; }; /** * varintEncode produce binary encoding of the integer number * @param {number} number integer number * @return {string} binary varint-encoded number */ var varintEncode = function(number) { // This only works correctly for numbers 0 through 16383 (0x3FFF) if (number <= 127) { return String.fromCharCode(number); } else { return String.fromCharCode(128 + (number & 0x7f), number >>> 7); } };
チャレンジに対する回答を生成する - Chrome 拡張機能は、受け取ったチャレンジを使用します。 使用して、enterprise.platformKeys Chrome API を呼び出します。この 署名付きの暗号化されたチャレンジ レスポンスを生成します。このチャレンジ レスポンスに、 ネットワーク サービスに送信するアクセス リクエストに含めます。
このステップでは、拡張機能とアプリケーションが ネットワーク サービスによる通信に使用されます。どちらのエンティティも 外部デベロッパーによって管理され、相互通信方法が規定されていません。「 (URL エンコードされた)チャレンジ レスポンスをクエリ文字列として送信する場合が考えられます。 HTTP POST または特別な HTTP ヘッダーを使用します。
チャレンジ レスポンスを生成するサンプルコードを次に示します。
チャレンジ レスポンスを生成する
// Generate challenge response var encodedChallenge; // obtained by generate challenge API call try { if (isDeviceVerification) { // isDeviceVerification set by external logic chrome.enterprise.platformKeys.challengeKey( { scope: 'MACHINE', challenge: decodestr2ab(encodedChallenge), }, ChallengeCallback); } else { chrome.enterprise.platformKeys.challengeKey( { scope: 'USER', challenge: decodestr2ab(encodedChallenge), registerKey: { 'RSA' }, // can also specify 'ECDSA' }, ChallengeCallback); } } catch (error) { console.log('ERROR: ' + error); }
チャレンジ コールバック関数
var ChallengeCallback = function(response) { if (chrome.runtime.lastError) { console.log(chrome.runtime.lastError.message); } else { var responseAsString = ab2base64str(response); console.log('resp: ' + responseAsString); ... // send on to network service }; }
ArrayBuffer 変換のヘルパーコード
/** * ab2base64str convert an ArrayBuffer to base64 string * @param {ArrayBuffer} buf ArrayBuffer instance * @return {string} base64 encoded string representation * of the ArrayBuffer */ var ab2base64str = function(buf) { var binary = ''; var bytes = new Uint8Array(buf); var len = bytes.byteLength; for (var i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } /** * decodestr2ab convert a base64 encoded string to ArrayBuffer * @param {string} str string instance * @return {ArrayBuffer} ArrayBuffer representation of the string */ var decodestr2ab = function(str) { var binary_string = window.atob(str); var len = binary_string.length; var bytes = new Uint8Array(len); for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes.buffer; }
チャレンジ レスポンスを確認 - (おそらくは既存の認証プロトコルの拡張機能として)、 ネットワーク サービスは Verified Access API を呼び出してデバイスを検証する必要がある (以下のサンプルコードを参照)。なりすましを防止するために、 ネットワーク サービスが通信先のクライアントを識別し、 想定されるクライアントの ID をリクエストに含めます。
- デバイスの確認では、想定されるデバイス ドメインを入力する必要があります。 をタップします。これは多くの場合、ハードコードされた値です。これは、ネットワーク 特定のドメインのリソースを保護します。不明な場合 ユーザー ID から推測できます。
- ユーザーの確認の場合、想定されるユーザーのメールアドレスは次のようになります。 表示されます。ネットワーク サービスがユーザー(通常は ユーザーがログインする必要があります)。
Google API は、呼び出されると、次のようないくつかのチェックを実行します。
- チャレンジ応答が ChromeOS によって生成されたものであり、 転送中に変更
- デバイスまたはユーザーが企業で管理されていることを確認します。
- デバイスまたはユーザーの ID が想定どおりであることを確認します。 できます(後者が指定されている場合)。
- 回答するチャレンジが 最新(1 分以内に完了)
- デバイスまたはユーザーが、 ドメイン管理者に説明します
- 呼び出し元(ネットワーク サービス)に呼び出し権限が付与されていることを確認する できます。
- 呼び出し元に追加のデバイスまたはデバイスを取得する権限が与えられているか ユーザーデータ(デバイス ID やユーザーの証明書署名など) (CSR)がレスポンスに含まれます。
この例では gRPC ライブラリを使用しています。
import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.chrome.verifiedaccess.v2.VerifiedAccessGrpc; import com.google.chrome.verifiedaccess.v2.VerifyChallengeResponseRequest; import com.google.chrome.verifiedaccess.v2.VerifyChallengeResponseResult; import com.google.protobuf.ByteString; import io.grpc.ClientInterceptor; import io.grpc.ClientInterceptors; import io.grpc.ManagedChannel; import io.grpc.auth.ClientAuthInterceptor; import io.grpc.netty.GrpcSslContexts; import io.grpc.netty.NettyChannelBuilder; import java.io.File; import java.io.FileInputStream; import java.util.Arrays; import java.util.concurrent.Executors; // https://cloud.google.com/storage/docs/authentication#generating-a-private-key private final String clientSecretFile = "PATH_TO_GENERATED_JSON_SECRET_FILE"; private ManagedChannel channel; private VerifiedAccessGrpc.VerifiedAccessBlockingStub client; void setup() { channel = NettyChannelBuilder.forAddress("verifiedaccess.googleapis.com", 443) .sslContext(GrpcSslContexts.forClient().ciphers(null).build()) .build(); List<ClientInterceptor> interceptors = Lists.newArrayList(); // Attach a credential for my service account and scope it for the API. GoogleCredentials credentials = ServiceAccountCredentials.class.cast( GoogleCredentials.fromStream( new FileInputStream(new File(clientSecretFile)))); credentials = credentials.createScoped( Arrays.<String>asList("https://www.googleapis.com/auth/verifiedaccess")); interceptors.add( new ClientAuthInterceptor(credentials, Executors.newSingleThreadExecutor())); // Create a stub bound to the channel with the interceptors applied client = VerifiedAccessGrpc.newBlockingStub( ClientInterceptors.intercept(channel, interceptors)); } /** * Invokes the synchronous RPC call that verifies the device response. * Returns the result protobuf as a string. * * @param signedData base64 encoded signedData blob (this is a response from device) * @param expectedIdentity expected identity (domain name or user email) * @return the verification result protobuf as string */ public String verifyChallengeResponse(String signedData, String expectedIdentity) throws IOException, io.grpc.StatusRuntimeException { VerifyChallengeResponseResult result = client.verifyChallengeResponse(newVerificationRequest(signedData, expectedIdentity)); // will throw StatusRuntimeException on error. return result.toString(); } private VerifyChallengeResponseRequest newVerificationRequest( String signedData, String expectedIdentity) throws IOException { return VerifyChallengeResponseRequest.newBuilder() .setChallengeResponse( ByteString.copyFrom(BaseEncoding.base64().decode(signedData))) .setExpectedIdentity(expectedIdentity == null ? "" : expectedIdentity) .build(); }
アクセス権を付与する - この手順はネットワーク サービス固有でもあります。これは 実装を推奨します。考えられるアクションは次のとおりです。
- セッション Cookie の作成
- ユーザーまたはデバイスの証明書の発行。ユーザーが成功した場合 ネットワーク サービスにアクセス権が付与されていることを前提としています。 (Google 管理コンソールのポリシーを介して)追加のユーザーデータにアクセスすることで、 ユーザーが署名した CSR を受信します。これを使用して実際の 証明書を取得することもできます。統合する場合 MicrosoftR CA の場合、ネットワーク サービスは 仲介者 ICertRequest インターフェースを使用できます
確認済みアクセスでのクライアント証明書の使用
<ph type="x-smartling-placeholder">大規模な組織では、複数のネットワーク サービスが存在する場合があります。 (VPN サーバー、Wi-Fi アクセス ポイント、ファイアウォール、複数のイントラネット サイト) 確認済みアクセスのメリットを享受できますただし、ステップのロジックを構築することは、 各ネットワーク サービスの 2 から 4 まで(上のセクションに記載)は、 実践的です。多くの場合、こうしたネットワーク サービスの多くは、 認証の一環としてクライアント証明書が要求されます(たとえば、 EAP-TLS または相互 TLS イントラネット ページ)。そのため、エンタープライズ証明書が これらのクライアント証明書を発行する機関は、手順 2 ~ 4 を実施し、 チャレンジ / レスポンスでクライアント証明書の発行を条件にする 証明書の所持が証拠として クライアントが正直で、会社のポリシーに準拠している。それ以降、Wi-Fi に接続される アクセス ポイント、VPN サーバーなどが、このクライアント証明書 手順 2 ~ 4 は不要です。
つまり、クライアント証明書を企業に発行する CA 図 1 のネットワーク サービスの役割を果たします。この関数を呼び出す必要があります。 Verified Access API へのアクセスと、チャレンジ レスポンスの検証時のみ クライアントに証明書を提供します。証明書の配布先 このクライアントは、図 1 のステップ 4 - アクセスを許可します。
Chromebook でクライアント証明書を安全に取得するプロセスについて説明する こちらの記事をご覧ください。もし この項で説明されている設計に従う場合は、確認済みアクセスの拡張機能が クライアント証明書のオンボーディング拡張機能は 1 つに統合できます。詳細 クライアント証明書オンボーディング拡張機能の作成方法についてご覧ください。