Entwicklerhandbuch für Chrome Verified Access

Übersicht

Mit der Chrome Verified Access API können Netzwerkdienste wie VPNs, Intranetseiten usw. kryptografisch überprüfen, ob ihre Clients echt sind und den Unternehmensrichtlinien entsprechen. Die meisten großen Unternehmen müssen nur von Unternehmen verwaltete Geräte in ihre WPA2-EAP-TLS-Netzwerke zulassen, einen höherstufigen Zugriff in VPNs und gegenseitige TLS-Intranetseiten. Viele vorhandene Lösungen stützen sich auf heuristische Prüfungen auf demselben Client, der möglicherweise manipuliert wurde. Dies stellt die Herausforderung dar, dass die Signale, die zur Bestätigung des legitimen Status des Geräts verwendet wurden, möglicherweise gefälscht sind. Dieser Leitfaden bietet hardwaregestützte kryptografische Garantien der Identität des Geräts sowie dafür, dass sein Status beim Start unverändert und richtlinienkonform war. Dies wird als verifizierter Zugriff bezeichnet.

Hauptzielgruppe IT-Domainadministratoren für Unternehmen
Technische Komponenten ChromeOS, Google Verified Access API

Voraussetzungen für den bestätigten Zugriff

Führen Sie die folgenden Schritte aus, bevor Sie den bestätigten Zugriff implementieren.

API aktivieren

Richten Sie ein Google API Console-Projekt ein und aktivieren Sie die API:

  1. Erstellen Sie ein Projekt in der Google API Console oder verwenden Sie ein vorhandenes Projekt.
  2. Rufen Sie die Seite Aktivierte APIs und Dienste auf.
  3. Aktivieren Sie die Chrome Verified Access API.
  4. Erstellen Sie einen API-Schlüssel für Ihre Anwendung. Folgen Sie dazu der Google Cloud API-Dokumentation.

Dienstkonto erstellen

Damit der Netzwerkdienst auf die Chrome Verified Access API zugreifen kann, um Ihre Challenge-Antwort zu prüfen, erstellen Sie ein Dienstkonto und einen Dienstkontoschlüssel. Sie müssen kein neues Cloud-Projekt erstellen, sondern können dasselbe verwenden.

Nachdem Sie den Dienstkontoschlüssel erstellt haben, sollten Sie eine private Schlüsseldatei für das Dienstkonto heruntergeladen haben. Dies ist die einzige Kopie des privaten Schlüssels. Bewahren Sie ihn daher sicher auf.

Verwaltetes Chromebook-Gerät registrieren

Sie benötigen ein ordnungsgemäß verwaltetes Chromebook-Gerät mit der Chrome-Erweiterung für den bestätigten Zugriff.

  1. Das Chromebook-Gerät muss für die Verwaltung durch Unternehmen oder Bildungseinrichtungen registriert sein.
  2. Der Nutzer des Geräts muss ein registrierter Nutzer derselben Domain sein.
  3. Die Chrome-Erweiterung für den bestätigten Zugriff muss auf dem Gerät installiert sein.
  4. Die Richtlinien sind so konfiguriert, dass der bestätigte Zugriff aktiviert, die Chrome-Erweiterung auf die Zulassungsliste gesetzt und dem Dienstkonto, das den Netzwerkdienst darstellt, Zugriff auf die API gewährt wird. Weitere Informationen finden Sie in der Hilfedokumentation zur Google Admin-Konsole.

Nutzer und Gerät bestätigen

Entwickler können den bestätigten Zugriff für die Nutzer- oder Gerätebestätigung oder beides für zusätzliche Sicherheit verwenden:

  • Gerätebestätigung: Mit der Gerätebestätigung wird sichergestellt, dass das Chrome-Gerät in einer verwalteten Domain registriert ist und der vom Domainadministrator festgelegten Geräterichtlinie für bestätigten Bootmodus entspricht. Wenn dem Netzwerkdienst die Berechtigung zum Aufrufen der Geräteidentität gewährt wurde (siehe Hilfedokumentation zur Google Admin-Konsole), erhält er auch eine Geräte-ID, die für die Prüfung, das Tracking oder den Aufruf der Directory API verwendet werden kann.

  • Nutzerbestätigung: Mit der Nutzerbestätigung ist sichergestellt, dass der angemeldete Chrome-Nutzer ein verwalteter Nutzer ist, ein registriertes Gerät verwendet und der vom Domainadministrator festgelegten Nutzerrichtlinie für bestätigten Bootmodus entspricht. Wenn dem Netzwerkdienst die Berechtigung zum Empfang zusätzlicher Nutzerdaten gewährt wird, erhält er auch eine vom Nutzer ausgestellte Anfrage zur Zertifikatssignierung (CSR in Form von Signed-public-key-and-challenge oder SPKAC, auch als Keygen-Format bezeichnet).

Nutzer und Gerät bestätigen

  1. Abfrage erhalten: Die Chrome-Erweiterung auf dem Gerät kontaktiert die Verified Access API, um eine Identitätsbestätigung zu erhalten. Die Aufgabe ist eine intransparente Datenstruktur (ein von Google signiertes Blob), die eine Minute lang gültig ist, d. h. die Überprüfung von Challenge-Antworten (Schritt 3) schlägt fehl, wenn eine veraltete Abfrage verwendet wird.

    Im einfachsten Anwendungsfall startet der Nutzer diesen Ablauf, indem er auf eine von der Erweiterung generierte Schaltfläche klickt. Das entspricht auch der von Google bereitgestellten Beispielerweiterung.

    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
      }
    };
    

    Hilfscode zum Codieren der Herausforderung: Wenn Sie Version 1 der API verwenden, muss die Aufgabe codiert werden.

    // 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);
      }
    };
    
  2. Abfrageantwort generieren: Die Chrome-Erweiterung ruft anhand der in Schritt 1 eingegangenen Identitätsbestätigung die Chrome API „enterprise.platformKeys“ auf. Dadurch wird eine signierte und verschlüsselte Antwort auf die Identitätsbestätigung generiert, die in die Zugriffsanfrage aufgenommen wird, die an den Netzwerkdienst gesendet wird.

    Bei diesem Schritt wird nicht versucht, ein Protokoll zu definieren, das die Erweiterung und der Netzwerkdienst für die Kommunikation verwenden. Beide Entitäten werden von externen Entwicklern implementiert und es ist nicht vorgegeben, wie sie miteinander kommunizieren. Ein Beispiel wäre das Senden einer (URL-codierten) Abfrageantwort als Abfragestringparameter, mithilfe von HTTP POST oder mit einem speziellen HTTP-Header.

    Hier ist ein Beispielcode zum Generieren einer Challenge-Antwort:

    Challenge-Antwort generieren

      // 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);
      }
    

    Challenge-Callback-Funktion

      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
       };
      }
    

    Hilfscode für die ArrayBuffer-Konvertierung

      /**
       * 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;
      }
    
  3. Abfrageantwort prüfen: Nach dem Empfang einer Identitätsbestätigung von einem Gerät (möglicherweise als Erweiterung für ein vorhandenes Authentifizierungsprotokoll) sollte der Netzwerkdienst die Verified Access API aufrufen, um die Geräteidentität und den Richtlinienstatus zu überprüfen (siehe Beispielcode unten). Zur Bekämpfung von Spoofing empfehlen wir, dass der Netzwerkdienst den Client, mit dem er kommuniziert, identifiziert und die erwartete Identität des Clients in seine Anfrage aufnimmt:

    • Zur Gerätebestätigung muss die erwartete Gerätedomain angegeben werden. In vielen Fällen ist dies wahrscheinlich ein hartcodierter Wert, da der Netzwerkdienst Ressourcen für eine bestimmte Domain schützt. Wenn dies nicht im Voraus bekannt ist, kann sie aus der Nutzeridentität abgeleitet werden.
    • Für die Nutzerbestätigung muss die E-Mail-Adresse des erwarteten Nutzers angegeben werden. Wir erwarten, dass der Netzwerkdienst seine Nutzer kennt (in der Regel müssen sich Nutzer anmelden).

    Wenn die Google API aufgerufen wird, führt sie eine Reihe von Prüfungen durch, z. B.:

    • Prüfen, ob die Challenge-Antwort von ChromeOS erstellt wurde und bei der Übertragung nicht geändert wird
    • Prüfen Sie, ob das Gerät oder der Nutzer vom Unternehmen verwaltet wird.
    • Überprüfe, ob die Identität des Geräts/Nutzers mit der erwarteten Identität übereinstimmt (falls diese angegeben ist).
    • Die Herausforderung, auf die geantwortet wird, muss aktuell sein (nicht älter als eine Minute).
    • Überprüfen Sie, ob das Gerät oder der Nutzer den Richtlinien des Domainadministrators entspricht.
    • Prüfen Sie, ob dem Aufrufer (Netzwerkdienst) die Berechtigung zum Aufrufen der API gewährt wurde.
    • Wenn dem Aufrufer die Berechtigung zum Abrufen zusätzlicher Geräte- oder Nutzerdaten gewährt wird, geben Sie in der Antwort die Geräte-ID oder die Anfrage zur Zertifikatsignierung (Certificate Signing Request, CSR) des Nutzers an.

    In diesem Beispiel wird die gRPC-Bibliothek verwendet.

    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();
    }
    
  4. Zugriff gewähren: Dieser Schritt bezieht sich auch auf einen Netzwerkdienst. Dies ist eine empfohlene (nicht vorgeschriebene) Implementierung. Mögliche Maßnahmen:

    • Erstellen eines Sitzungscookies
    • Ein Zertifikat für den Nutzer oder das Gerät ausstellen Bei erfolgreicher Nutzerbestätigung und unter der Annahme, dass dem Netzwerkdienst über die Richtlinie der Google Admin-Konsole Zugriff auf zusätzliche Nutzerdaten gewährt wurde, erhält er eine vom Nutzer signierte CSR, mit der er das tatsächliche Zertifikat der Zertifizierungsstelle abrufen kann. Bei der Einbindung in Microsoft CA kann der Netzwerkdienst als Vermittler fungieren und die ICertRequest-Schnittstelle nutzen.

Clientzertifikate mit bestätigtem Zugriff verwenden

Clientzertifikate mit bestätigtem Zugriff verwenden.

In einem großen Unternehmen kann es mehrere Netzwerkdienste geben, die von dem bestätigten Zugriff profitieren würden (VPN-Server, WLAN-Zugangspunkte, Firewalls und mehrere Intranet-Websites). Es ist jedoch möglicherweise nicht zweckmäßig, die Logik der Schritte 2 bis 4 (im obigen Abschnitt) in jedem dieser Netzwerkdienste zu erstellen. Viele dieser Netzwerkdienste haben bereits die Möglichkeit, Clientzertifikate als Teil ihrer Authentifizierung anzufordern (z. B. EAP-TLS oder gegenseitige TLS-Intranetseiten). Wenn also die Enterprise-Zertifizierungsstelle, die diese Clientzertifikate ausstellt, die Schritte 2 bis 4 implementieren und die Ausstellung des Clientzertifikats für die Challenge-Response-Bestätigung konditionieren könnte, könnte der Besitz des Zertifikats ein Nachweis dafür sein, dass der Client echt ist und der Unternehmensrichtlinie entspricht. Danach können WLAN-Zugangspunkte, VPN-Server usw. nach diesem Clientzertifikat suchen, anstatt die Schritte 2 bis 4 ausführen zu müssen.

Mit anderen Worten, hier übernimmt die Zertifizierungsstelle (die das Clientzertifikat an Unternehmensgeräte ausstellt) die Rolle des Netzwerkdienstes in Abbildung 1. Er muss die Verified Access API aufrufen und dem Client das Zertifikat erst zur Verfügung stellen, wenn die Verifizierung der Identitätsbestätigung erfolgreich war. Die Bereitstellung des Zertifikats für den Client entspricht dem 4. Schritt „Zugriff gewähren“ in Abbildung 1.

Wie Clientzertifikate sicher auf Chromebooks übertragen werden, wird in diesem Artikel beschrieben. Wenn das in diesem Abschnitt beschriebene Design eingehalten wird, können die Erweiterung für den bestätigten Zugriff und die Onboarding-Erweiterung für Clientzertifikate zu einem kombiniert werden. Weitere Informationen zum Schreiben einer Onboarding-Erweiterung für Clientzertifikate