Guida per gli sviluppatori all'accesso verificato di Chrome

Informazioni sulla guida

L'API Chrome Verified Access consente a servizi di rete quali VPN, pagine intranet e così via di verificare crittograficamente che i loro client siano originali e conformi alle norme aziendali. La maggior parte delle grandi aziende ha il requisito di consentire solo i dispositivi gestiti dall'azienda sulle proprie reti WPA2 EAP-TLS, sull'accesso di livello superiore nelle VPN e sulle pagine Intranet con TLS reciproco. Molte soluzioni esistenti si basano su controlli euristici sullo stesso cliente che potrebbe essere stato compromesso. Ciò rappresenta la sfida per cui gli indicatori utilizzati per attestare lo stato legittimo del dispositivo potrebbero essere stati falsificati. Questa guida fornisce garanzie crittografiche basate su hardware che identificano il dispositivo e che il suo stato non è stato modificato e conforme ai criteri all'avvio. Questa guida è chiamata Accesso verificato.

Pubblico principale Amministratori di domini IT aziendali
Componenti tecnici ChromeOS, API Google Verified Access

Prerequisiti per l'Accesso verificato

Completa la configurazione seguente prima di implementare il processo di Accesso verificato.

Abilita l'API

Configura un progetto nella console API di Google e abilita l'API:

  1. Crea o utilizza un progetto esistente nella console API di Google.
  2. Vai alla pagina API e servizi abilitati.
  3. Attiva l'API Chrome Verified Access.
  4. Crea una chiave API per la tua applicazione seguendo la documentazione relativa all'API Google Cloud.

Creare un account di servizio

Affinché il servizio di rete possa accedere all'API Chrome Verified Access per verificare la tua risposta alla sfida, crea un account di servizio e una chiave dell'account di servizio (non è necessario creare un nuovo progetto Cloud, puoi utilizzare lo stesso).

Dopo aver creato la chiave dell'account di servizio, dovresti aver scaricato un file della chiave privata dell'account di servizio. Questa è l'unica copia della chiave privata, perciò assicurati di conservarla in modo sicuro.

Registrare un dispositivo Chromebook gestito

Devi configurare un dispositivo Chromebook gestito correttamente con l'estensione di Chrome per Accesso verificato.

  1. Il dispositivo Chromebook deve essere registrato per la gestione aziendale o per la didattica.
  2. L'utente del dispositivo deve essere un utente registrato dello stesso dominio.
  3. L'estensione di Chrome per Accesso verificato deve essere installata sul dispositivo.
  4. I criteri sono configurati per attivare Accesso verificato, inserire l'estensione di Chrome nella lista consentita e concedere l'accesso all'API per l'account di servizio che rappresenta il servizio di rete (consulta la documentazione di assistenza della Console di amministrazione Google).

Verifica utente e dispositivo

Gli sviluppatori possono utilizzare l'Accesso verificato per la verifica dell'utente o del dispositivo oppure utilizzare entrambi per una maggiore sicurezza:

  • Verifica del dispositivo: se l'operazione ha esito positivo, la verifica del dispositivo garantisce che il dispositivo Chrome sia registrato in un dominio gestito e che sia conforme ai criteri relativi ai dispositivi in modalità di avvio verificato, come specificato dall'amministratore di dominio. Se al servizio di rete viene concessa l'autorizzazione per visualizzare l'identità del dispositivo (consulta la documentazione della Guida della Console di amministrazione Google), riceve anche un ID dispositivo che può essere utilizzato per il controllo, il monitoraggio o la chiamata all'API Directory.

  • Verifica dell'utente: in caso di esito positivo, la verifica utente garantisce che un utente di Chrome che ha eseguito l'accesso sia un utente gestito, utilizzi un dispositivo registrato e sia conforme ai criteri relativi agli utenti in modalità di avvio verificato, come specificato dall'amministratore di dominio. Se al servizio di rete viene concessa l'autorizzazione a ricevere ulteriori dati utente, otterrà anche una richiesta di firma del certificato emessa dall'utente (CSR sotto forma di Signed-public-key-and-challenge, o SPKAC, noto anche come formato keygen).

Come verificare utente e dispositivo

  1. Richiesta di una verifica: l'estensione di Chrome sul dispositivo contatta l'API Verified Access per ottenere una verifica. La sfida è una struttura di dati opaca (un blob firmato da Google) valida per un minuto, il che significa che la verifica della risposta alla sfida (passaggio 3) non va a buon fine se viene utilizzata una verifica obsoleta.

    Nel caso d'uso più semplice, l'utente avvia questo flusso facendo clic su un pulsante generato dall'estensione (questo è anche ciò che fa l'estensione di esempio fornita da 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
      }
    };
    

    Verifica tramite codice di supporto per la codifica: se utilizzi la versione 1 dell'API, la verifica dovrà essere codificata.

    // 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. Generare una risposta alla sfida: l'estensione di Chrome utilizza la sfida ricevuta nel passaggio 1 per chiamare l'API enterprise.platformKeys di Chrome. In questo modo viene generata una risposta di richiesta firmata e criptata, che l'estensione include nella richiesta di accesso che invia al servizio di rete.

    In questo passaggio, non viene effettuato alcun tentativo di definire un protocollo utilizzato dall'estensione e dal servizio di rete per la comunicazione. Entrambe queste entità sono implementate da sviluppatori esterni e non viene specificato un modo in cui interagiscono tra loro. Un esempio potrebbe essere l'invio di una risposta di verifica (con codifica URL) come parametro di stringa di query, utilizzando HTTP POST o un'intestazione HTTP speciale.

    Di seguito è riportato un codice campione per generare una risposta alla sfida:

    Generare risposte alle sfide

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

    Funzione di callback della verifica

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

    Codice di assistenza per la conversione Arraybu

      /**
       * 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. Verifica della risposta di richiesta: dopo aver ricevuto una risposta di verifica da un dispositivo (ad esempio come estensione di un protocollo di autenticazione esistente), il servizio di rete deve chiamare l'API Verified Access per verificare l'identità del dispositivo e la strategia dei criteri (vedi il codice di esempio di seguito). Per contrastare lo spoofing, consigliamo che il servizio di rete identifichi il client con cui comunica e includa l'identità prevista del client nella sua richiesta:

    • Per la verifica del dispositivo, è necessario fornire il dominio previsto del dispositivo. In molti casi si tratta probabilmente di un valore hardcoded, perché il servizio di rete protegge le risorse per un determinato dominio. Se i dati non sono noti in anticipo, possono essere dedotti dall'identità dell'utente.
    • Per la verifica dell'utente, deve essere fornito l'indirizzo email dell'utente previsto. Prevediamo che il servizio di rete conosca i suoi utenti (normalmente è richiesto agli utenti di eseguire l'accesso).

    Quando viene chiamata, l'API di Google esegue una serie di controlli, ad esempio:

    • Verifica che la risposta alla sfida sia prodotta da ChromeOS e non sia modificata durante il transito
    • Verifica che il dispositivo o l'utente sia gestito dall'azienda.
    • Verifica che l'identità del dispositivo/utente corrisponda all'identità prevista (se quest'ultima è fornita).
    • Verifica che la sfida a cui viene data risposta sia aggiornata (non più di un minuto prima).
    • Verifica che il dispositivo o l'utente sia conforme alle norme specificate dall'amministratore di dominio.
    • Verifica che al chiamante (servizio di rete) sia concessa l'autorizzazione per chiamare l'API.
    • Se al chiamante viene concessa l'autorizzazione per ottenere dati utente o del dispositivo aggiuntivi, includi l'ID dispositivo o la richiesta di firma del certificato (CSR) dell'utente nella risposta.

    In questo esempio viene utilizzata la libreria 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. Concedi l'accesso: questo passaggio è specifico anche per il servizio di rete. Questa è un'implementazione suggerita (non prescritta). Le possibili azioni potrebbero essere:

    • Creazione di un cookie di sessione
    • Emissione di un certificato per l'utente o il dispositivo. In caso di esito positivo della verifica dell'utente e supponendo che al servizio di rete sia stato concesso l'accesso a dati utente aggiuntivi (tramite i criteri della Console di amministrazione Google), riceve una richiesta di firma dell'utente firmata dall'utente, che può essere utilizzata per ottenere il certificato effettivo dall'autorità di certificazione. In fase di integrazione con la CA Microsoft, il servizio di rete potrebbe fungere da intermediario e utilizzare l'interfaccia ICertRequest.

Utilizzo dei certificati client con Accesso verificato

Utilizzo di certificati client con Accesso verificato.

In una grande organizzazione, potrebbero esistere più servizi di rete (server VPN, punti di accesso Wi-Fi, firewall e più siti intranet) che potrebbero trarre vantaggio dall'accesso verificato. Tuttavia, creare la logica dei passaggi 2-4 (nella sezione precedente) in ciascuno di questi servizi di rete potrebbe non essere pratico. Spesso, molti di questi servizi di rete sono già in grado di richiedere certificati client come parte delle autenticazioni (ad esempio, pagine EAP-TLS o intranet TLS reciproca). Quindi, se l'autorità di certificazione aziendale che rilascia questi certificati client potrebbe implementare i passaggi 2-4 e condizionare l'emissione del certificato client nella verifica della risposta challenge, il possesso del certificato potrebbe essere la prova che il client è autentico e conforme alla politica aziendale. Da quel momento in poi ogni punto di accesso Wi-Fi, server VPN e così via potrebbero verificare la presenza di questo certificato client anziché dover seguire i passaggi 2-4.

In altre parole, qui la CA (che rilascia il certificato client ai dispositivi aziendali) assume il ruolo del servizio di rete nella Figura 1. Deve richiamare l'API Verified Access e, solo dopo il superamento della verifica della risposta di verifica, fornire il certificato al client. Fornire il certificato al client equivale al passaggio 4 - Concedi l'accesso nella Figura 1.

La procedura per ottenere certificati client in modo sicuro sui Chromebook è descritta in questo articolo. Se la progettazione descritta in questo paragrafo viene seguita, l'estensione di accesso verificato e l'estensione di onboarding dei certificati client possono essere combinate in una sola. Scopri di più su come scrivere un'estensione di onboarding del certificato client.