Guide du développeur de l'accès validé à Chrome

À propos de ce guide

L'API Chrome Verified Access permet aux services réseau, tels que les VPN, les pages intranet, etc., de vérifier de manière cryptographique que leurs clients sont authentiques et conformes aux règles de l'entreprise. La plupart des grandes entreprises ont l'obligation d'autoriser uniquement les appareils gérés par l'entreprise sur leurs réseaux WPA2 EAP-TLS, l'accès de niveau supérieur aux VPN et les pages intranet mutuelles TLS. De nombreuses solutions existantes s'appuient sur des contrôles heuristiques sur le même client qui a pu être compromis. Il est donc possible que les signaux utilisés pour déterminer l'état légitime de l'appareil aient été falsifiés. Ce guide fournit des garanties cryptographiques intégrées à l'identité de l'appareil, ainsi qu'à l'absence de modification de son état et à la conformité aux règles au démarrage. C'est ce qu'on appelle la fonctionnalité d'accès validé.

Audience principale Administrateurs de domaines informatiques d'entreprise
Composants techniques ChromeOS, API Google Verified Access

Conditions préalables à la validation de l'accès

Effectuez la configuration suivante avant de mettre en œuvre la procédure de validation de l'accès.

Activer l'API

Configurez un projet dans la console Google APIs et activez l'API:

  1. Créez un projet ou utilisez un projet existant dans la console Google APIs.
  2. Accédez à la page API et services activés.
  3. Activez l'API Chrome Verified Access.
  4. Créez une clé API pour votre application en suivant la documentation de l'API Google Cloud.

Créer un compte de service

Pour que le service réseau accède à l'API Chrome Verified Access pour vérifier la réponse à votre défi, créez un compte de service et une clé de compte de service (vous n'avez pas besoin de créer un projet Cloud, vous pouvez utiliser le même).

Une fois la clé de compte de service créée, vous devez télécharger le fichier de clé privée de ce compte. Il s'agit de la seule copie de la clé privée. Veillez donc à la conserver en sécurité.

Enregistrer un Chromebook géré

Vous devez disposer d'une configuration d'appareil Chromebook correctement gérée avec votre extension Chrome pour l'accès validé.

  1. L'appareil Chromebook doit être enregistré pour la gestion par une entreprise ou un établissement scolaire.
  2. L'utilisateur de l'appareil doit être un utilisateur enregistré appartenant au même domaine.
  3. L'extension Chrome pour l'accès validé doit être installée sur l'appareil.
  4. Les règles sont configurées pour activer l'accès validé, ajouter l'extension Chrome à la liste d'autorisation et accorder l'accès à l'API au compte de service représentant le service réseau (consultez la documentation d'aide de la console d'administration Google).

Valider l'utilisateur et l'appareil

Les développeurs peuvent utiliser l'accès validé pour valider l'utilisateur ou l'appareil, ou les deux pour renforcer la sécurité:

  • Validation de l'appareil : en cas de réussite, la validation de l'appareil garantit que l'appareil Chrome est enregistré dans un domaine géré et qu'il est conforme à la règle relative aux appareils en mode de démarrage validé spécifiée par l'administrateur du domaine. Si le service réseau est autorisé à voir l'identité de l'appareil (consultez la documentation d'aide de la console d'administration Google), il reçoit également un ID d'appareil pouvant être utilisé pour l'audit, le suivi ou l'appel de l'API Directory.

  • Validation de l'utilisateur : si la validation de l'utilisateur aboutit, la validation de l'utilisateur garantit que l'utilisateur connecté à Chrome est un utilisateur géré, qu'il utilise un appareil enregistré et qu'il est conforme à la règle utilisateur du mode de démarrage validé spécifiée par l'administrateur du domaine. Si le service réseau obtient l'autorisation de recevoir des données utilisateur supplémentaires, il obtiendra également une requête de signature de certificat émise par l'utilisateur (CSR sous la forme de clé publique signée et de défi, ou SPKAC, également appelé format keygen).

Comment vérifier l'utilisateur et l'appareil

  1. Obtenir un test d'authentification : l'extension Chrome de l'appareil contacte l'API Verified Access pour obtenir un test d'authentification. Le défi est une structure de données opaque (un blob signé par Google) d'une durée d'utilisation d'une minute, ce qui signifie que la vérification du défi-réponse (étape 3) échoue si un test obsolète est utilisé.

    Dans le cas d'utilisation le plus simple, l'utilisateur lance ce flux en cliquant sur un bouton généré par l'extension (c'est également ce que fait l'exemple d'extension fourni par 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
      }
    };
    

    Défi d'encodage pour l'encodage : si vous utilisez la version 1 de l'API, le défi devra être encodé.

    // 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. Générer une réponse à un test : l'extension Chrome utilise le défi reçu à l'étape 1 pour appeler l'API Chrome enterprise.platformKeys Vous générez ainsi une réponse à la question signée et chiffrée, que l'extension inclut dans la requête d'accès qu'elle envoie au service réseau.

    Au cours de cette étape, vous n'essayez pas de définir un protocole que l'extension et le service réseau utilisent pour communiquer. Ces deux entités sont mises en œuvre par des développeurs externes et ne sont pas soumises à la façon dont elles communiquent entre elles. Par exemple, vous pouvez envoyer une réponse à la question (encodée en URL) sous la forme d'un paramètre de chaîne de requête, via HTTP POST ou en utilisant un en-tête HTTP spécial.

    Voici un exemple de code permettant de générer une réponse à un défi:

    Générer une réponse au défi

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

    Fonction de rappel de question

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

    Code d'aide pour la conversion 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;
      }
    
  3. Vérifier la réponse à la question d'authentification : lorsque l'appareil reçoit une réponse à la question d'authentification (peut-être en extension d'un protocole d'authentification existant), le service réseau doit appeler l'API Verified Access pour vérifier l'identité de l'appareil et la stratégie de sécurité (voir l'exemple de code ci-dessous). Pour lutter contre le spoofing, nous recommandons au service réseau d'identifier le client avec lequel il s'adresse et d'inclure l'identité attendue du client dans sa requête:

    • Pour la validation des appareils, vous devez indiquer le domaine attendu de l'appareil. Dans de nombreux cas, il s'agit probablement d'une valeur codée en dur, car le service réseau protège les ressources d'un domaine particulier. Si cette information n'est pas connue à l'avance, elle peut être déduite de l'identité de l'utilisateur.
    • Pour la validation de l'utilisateur, l'adresse e-mail de l'utilisateur attendu doit être fournie. Nous nous attendons à ce que le service réseau connaisse ses utilisateurs (généralement, il doit leur demander de se connecter).

    Lorsque l'API Google est appelée, elle effectue un certain nombre de vérifications, par exemple:

    • Vérifier que la réponse à la question d'authentification est produite par ChromeOS et n'est pas modifiée en transit
    • Vérifiez que l'appareil ou l'utilisateur sont gérés par l'entreprise.
    • Vérifiez que l'identité de l'appareil/de l'utilisateur correspond à l'identité attendue (si celle-ci est fournie).
    • Vérifiez que le défi auquel l'utilisateur répond est récent (datant d'une minute maximum).
    • Vérifiez que l'appareil ou l'utilisateur respecte les règles spécifiées par l'administrateur du domaine.
    • Vérifiez que l'appelant (service réseau) est autorisé à appeler l'API.
    • Si l'appelant est autorisé à obtenir des données utilisateur ou d'appareil supplémentaires, incluez l'ID de l'appareil ou la requête de signature de certificat de l'utilisateur dans la réponse.

    Cet exemple utilise la bibliothèque 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();
    }
    
  4. Accorder l'accès : cette étape est également spécifique au service réseau. Il s'agit d'une implémentation suggérée (non prescrite). Voici les actions possibles:

    • Création d'un cookie de session
    • Émettre un certificat pour l'utilisateur ou l'appareil En cas de validation réussie de l'utilisateur, et en supposant que le service réseau ait accès à des données utilisateur supplémentaires (via les règles de la console d'administration Google), il reçoit une requête de signature de certificat signée par l'utilisateur, qui peut ensuite être utilisée pour obtenir le certificat réel de l'autorité de certification. Lors de l'intégration à l'autorité de certification Microsoft, le service réseau peut agir en tant qu'intermédiaire et utiliser l'interface ICertRequest.

Utiliser des certificats client avec l'accès validé

Utilisez des certificats client avec l'accès validé.

Dans une grande organisation, plusieurs services réseau (serveurs VPN, points d'accès Wi-Fi, pare-feu et plusieurs sites intranet) peuvent bénéficier de la fonctionnalité de vérification d'accès. Toutefois, la création de la logique des étapes 2 à 4 (dans la section ci-dessus) dans chacun de ces services réseau peut s'avérer difficile. Souvent, ces services réseau sont déjà capables d'exiger des certificats client dans le cadre de leurs authentifications (par exemple, les pages intranet EAP-TLS ou les pages intranet TLS mutuelles). Ainsi, si l'autorité de certification d'entreprise qui émet ces certificats clients peut mettre en œuvre les étapes 2 à 4 et conditionner l'émission du certificat client à la vérification de défi-réponse, la possession du certificat peut être la preuve que le client est authentique et conforme aux règles de l'entreprise. Chaque point d'accès Wi-Fi, serveur VPN, etc. peut ensuite rechercher ce certificat client au lieu de devoir suivre les étapes 2 à 4.

En d'autres termes, l'autorité de certification (qui émet le certificat client pour les appareils de l'entreprise) joue le rôle du service réseau dans la figure 1. Il doit appeler l'API Verified Access et fournir le certificat au client uniquement une fois la vérification de la réponse à un défi réussi. La fourniture du certificat au client équivaut à l'étape 4 : Accorder l'accès, comme illustré dans la figure 1.

Le processus d'obtention des certificats client sur les Chromebooks est décrit dans cet article. Si la conception décrite dans ce paragraphe est respectée, les extensions d'accès validé et d'intégration de certificats client peuvent être combinées en une seule et même extension. Découvrez comment écrire une extension d'intégration de certificats client.