Guia do desenvolvedor do Acesso verificado do Chrome

Sobre este guia

A API Chrome Verified Access permite serviços de rede, como VPNs, intranet páginas, e assim por diante, para verificar criptograficamente se seus clientes são genuínos e estão em conformidade com a política corporativa. A maioria das grandes empresas precisa permitir que apenas dispositivos gerenciados pela empresa acessem as redes WPA2 EAP-TLS, acesso de nível superior em VPNs e páginas de intranet de TLS mútuo. Muitos soluções dependem de verificações heurísticas do mesmo cliente, que podem ter sido comprometido. Isso apresenta o desafio em que os sinais confiáveis atestem o status legítimo do dispositivo podem ter sido falsificado. Este guia oferece garantias criptográficas com suporte de hardware da identidade do dispositivo e que o estado dele não foi modificado e está em conformidade com a política na inicialização. chamada "Acesso verificado".

Público principal Administradores de domínios de TI corporativos
Componentes técnicos ChromeOS, API Google Verified Access

Pré-requisitos do "Acesso verificado"

Conclua a configuração a seguir antes de implementar o processo de "Acesso verificado".

Ativar a API

Configure um projeto do Console de APIs do Google e ative a API:

  1. Crie ou use um projeto existente no Console de APIs do Google.
  2. Acesse o APIs ativadas e de serviço.
  3. Ative a API Chrome Verified Access.
  4. Crie uma chave de API para o aplicativo seguindo a documentação da API Google Cloud.

Criar uma conta de serviço

Para que o serviço de rede acesse a API Verified Access do Chrome para verificar seu resposta ao desafio, Criar uma conta de serviço e uma chave de conta de serviço Não é necessário criar um novo projeto do Cloud, mas é possível que você use o mesmo projeto.

Depois de criar a chave da conta de serviço, você terá uma conta de serviço o download do arquivo de chave privada. Essa é a única cópia da chave privada, portanto, você deve armazená-lo com segurança.

Registrar um dispositivo Chromebook gerenciado

É preciso ter um dispositivo Chromebook gerenciado corretamente com o Chrome para "Acesso verificado".

  1. O dispositivo Chromebook precisa estar registrado para o gerenciamento empresarial ou educacional.
  2. O usuário do dispositivo precisa ser um usuário registrado no mesmo domínio.
  3. A extensão do Chrome para "Acesso verificado" precisa estar instalada no dispositivo.
  4. As políticas são configuradas para ativar o "Acesso verificado" e permitir o acesso do Chrome de serviço e conceda acesso à API para a conta de serviço que representa serviço de rede (consulte a Documentação de ajuda do Google Admin Console).

Verificar usuário e dispositivo

Os desenvolvedores podem usar o "Acesso verificado" para verificação de usuários ou dispositivos ou ambos para aumentar a segurança:

  • Verificação do dispositivo: se for bem-sucedida, a verificação do dispositivo fornecerá uma garantir que o dispositivo Chrome esteja registrado em um domínio gerenciado e que ele obedece à política do dispositivo no modo de inicialização verificada, conforme especificado pelo domínio administrador. Se o serviço de rede tiver permissão para ver o dispositivo identidade (consulte a documentação de ajuda do Google Admin Console), ele também recebe um ID do dispositivo, que pode ser usado para auditoria, acompanhamento ou chamada da API Directory.

  • Verificação do usuário: se a verificação for concluída, a verificação do usuário vai fornecer uma garantia. que um usuário conectado do Chrome é um usuário gerenciado, está usando um dispositivo registrado e obedece à política do usuário do modo de inicialização verificada, conforme especificado pelo domínio administrador. Se o serviço de rede tiver permissão para receber dados adicionais do usuário, ele também recebe uma solicitação de assinatura de certificado pelo usuário (CSR na forma de chave pública-e-desafio assinada, ou SPKAC, também conhecido como formato keygen).

Como verificar o usuário e o dispositivo

  1. Receber um desafio: a extensão do Chrome no dispositivo entra em contato com o Access API para receber um desafio. O desafio é um conjunto de dados (um blob assinado pelo Google) com duração de 1 minuto, ou seja, a verificação de resposta/desafio (etapa 3) falhará se um desafio obsoleto for usado.

    No caso de uso mais simples, o usuário inicia esse fluxo clicando em um botão que gerada pela extensão (é isso que a extensão de exemplo fornecida pelo Google tem).

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

    Código auxiliar para codificar o desafio: se você estiver usando a v1 da API, os desafio terá de ser codificado.

    // 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. Gerar uma resposta do desafio: a extensão do Chrome usa o desafio. recebidas na etapa 1 para chamar a API enterprise.platformKeys do Chrome. Isso gera uma resposta do desafio assinada e criptografada, que a extensão inclui na solicitação de acesso que envia ao serviço de rede.

    Nesta etapa, não há tentativa de definir um protocolo que a extensão e e serviços de rede para comunicação. Ambas as entidades são implementadas por desenvolvedores externos e não há regras de como eles se comunicam. Um exemplo seria enviar uma resposta de desafio (codificada para URL) como uma string de consulta , usando POST HTTP ou um cabeçalho HTTP especial.

    Este é um exemplo de código para gerar uma resposta do desafio:

    Gerar resposta ao desafio

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

    Função de callback do desafio

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

    Código auxiliar para conversão do 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. Verificar a resposta do desafio: ao receber a resposta do desafio de um dispositivo (talvez como uma extensão de um protocolo de autenticação existente), o serviço de rede precisa chamar a API Verified Access para verificar o dispositivo identidade e postura de política (veja o código de exemplo abaixo). Para combater a falsificação, recomendamos que o serviço de rede identifique o cliente com quem está se comunicando inclua a identidade esperada do cliente na solicitação:

    • Para a verificação do dispositivo, informe o domínio do dispositivo esperado. , Em muitos casos, esse é um valor codificado, porque a rede protege os recursos de um determinado domínio. Se você não souber com antecedência, ela pode ser inferida a partir da identidade do usuário.
    • Para a verificação do usuário, use o endereço de e-mail esperado. fornecidas. Esperamos que o serviço de rede conheça seus usuários (normalmente ele exigiria que os usuários fizessem login).

    Quando a API do Google é chamada, ela realiza várias verificações, como:

    • Verificar se a resposta do desafio é produzida pelo ChromeOS e não é modificado em trânsito
    • Verifique se o dispositivo ou usuário é gerenciado por uma empresa.
    • Verifique se a identidade do dispositivo/usuário corresponde ao identidade (se a segunda for fornecida).
    • Verificar se o desafio respondido é recentes (no máximo 1 minuto).
    • Verifique se o dispositivo ou usuário está em conformidade com a política conforme especificado por o administrador do domínio.
    • Verificar se o autor da chamada (serviço de rede) tem permissão para chamar a API.
    • Se o autor da chamada tiver permissão para obter dispositivos ou dados do usuário, inclua o ID do dispositivo ou a assinatura de certificado (CSR) na resposta.

    Este exemplo usa a biblioteca 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. Conceder acesso: esta etapa também é específica para o serviço de rede. Esta é uma uma implementação sugerida (não prescrita). Estas são as possíveis ações:

    • Criação de um cookie de sessão
    • Emitir um certificado para o usuário ou dispositivo. Em caso de êxito do usuário e presumindo que o serviço de rede tenha recebido acesso a dados adicionais do usuário (pela política do Google Admin Console), recebe uma CSR assinada por usuário, que pode então ser usada para obter a solicitação real certificado da autoridade certificadora. Ao integrar com MicrosoftR CA, o serviço de rede pode atuar um intermediário e usar a interface ICertRequest.

Como usar certificados do cliente com Acesso verificado

Como usar certificados do cliente com o Acesso verificado.

Em uma grande organização, pode haver vários serviços de rede (servidores VPN, pontos de acesso Wi-Fi, firewalls e vários sites da intranet) que com o recurso "Acesso verificado". No entanto, criar a lógica das etapas 2 a 4 (na seção acima) em cada um desses serviços de rede podem não ser práticos. Muitas vezes, muitos desses serviços de rede já têm a capacidade de exigir certificados do cliente como parte das autenticações (por exemplo, EAP-TLS ou páginas de intranet de TLS mútuo). Portanto, se o certificado empresarial A autoridade que emite esses certificados do cliente pode implementar as etapas de 2 a 4 e condição para a emissão do certificado do cliente no desafio/resposta verificação, a posse do certificado pode ser a prova de que o cliente é verdadeiro e está em conformidade com a política corporativa. Depois disso, cada rede Wi-Fi ponto de acesso, servidor VPN e assim por diante poderiam verificar este certificado do cliente em vez de seguir as etapas de 2 a 4.

Em outras palavras, aqui é a CA (que emite o certificado do cliente para a ) assume o papel do Serviço de rede na Figura 1. Ele precisa invocar a API Verified Access e somente após a verificação da resposta ao desafio forneça o certificado ao cliente. Fornecer o certificado para o cliente é o equivalente à Etapa 4: conceder acesso na figura 1.

O processo de obtenção de certificados do cliente nos Chromebooks é descrito neste artigo. Se o o design descrito neste parágrafo é seguido, depois a Extensão de acesso verificado e a extensão de integração de certificado do cliente podem ser combinadas em um só. Saiba mais sobre como criar uma extensão de integração de certificado do cliente.