Autenticazione degli utenti con Cloud Identity-Aware Proxy per Python

Le app in esecuzione su piattaforme gestite da Google Cloud come App Engine possono evitare di gestire l'autenticazione degli utenti e la gestione delle sessioni utilizzando Identity-Aware Proxy (IAP) per controllarne l'accesso. IAP non solo può controllare l'accesso all'applicazione, ma offre anche informazioni sugli utenti autenticati, inclusi l'indirizzo email e un identificatore persistente all'app sotto forma di nuove intestazioni HTTP.

Obiettivi

  • Richiedi agli utenti della tua app App Engine di autenticarsi utilizzando IAP.

  • Accedi alle identità degli utenti nell'app per visualizzare l'indirizzo email autenticato dell'utente corrente.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Installa Google Cloud CLI.
  4. Per initialize gcloud CLI, esegui questo comando:

    gcloud init
  5. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  6. Installa Google Cloud CLI.
  7. Per initialize gcloud CLI, esegui questo comando:

    gcloud init

Contesto

Questo tutorial utilizza IAP per autenticare gli utenti. Questo è solo uno dei diversi approcci possibili. Per saperne di più sui vari metodi per autenticare gli utenti, consulta la sezione Concetti di autenticazione.

L'app Hello user-email-address

L'applicazione per questo tutorial è un'applicazione minima di Hello Engine di App Engine, con una funzionalità non tipica: invece di"Hello World", viene visualizzato"Hello user-email-address", dove user-email-address è l'indirizzo email autenticato dell'utente.

Questa funzionalità è possibile esaminando le informazioni autenticate che IAP aggiunge a ogni richiesta web che trasmette alla tua app. A ogni richiesta web aggiunta a ogni richiesta web vengono aggiunte tre nuove intestazioni. Le prime due intestazioni sono stringhe di testo normale che puoi utilizzare per identificare l'utente. La terza intestazione è un oggetto firmato in modo crittografico con le stesse informazioni.

  • X-Goog-Authenticated-User-Email: l'indirizzo email di un utente lo identifica. Non memorizzare informazioni personali se la tua app può evitarle. Questa app non memorizza alcun dato, ma lo fa eco all'utente.

  • X-Goog-Authenticated-User-Id: questo ID utente assegnato da Google non mostra le informazioni sull'utente, ma consente a un'app di sapere che un utente che ha eseguito l'accesso è lo stesso che era stato visualizzato in precedenza.

  • X-Goog-Iap-Jwt-Assertion: puoi configurare le app Google Cloud in modo che accetti richieste web da altre app cloud, bypassando gli IAP, oltre alle richieste web Internet. Se un'app è così configurata, è possibile che tali richieste abbiano intestazioni contraffatte. Invece di utilizzare le intestazioni di testo normale menzionate in precedenza, puoi utilizzare e verificare questa intestazione firmata crittografica per controllare che le informazioni siano state fornite da Google. Sia l'indirizzo email dell'utente sia uno User-ID permanente sono disponibili nell'ambito di questa intestazione firmata.

Se hai la certezza che l'app sia configurata in modo che solo le richieste web Internet possano raggiungerla e che nessuno possa disattivare il servizio IAP per l'app, il recupero di uno User-ID univoco richiede una sola riga di codice:

user_id = request.headers.get('X-Goog-Authenticated-User-ID')

Tuttavia, un'app resiliente dovrebbe andare storta, inclusi problemi di configurazione o ambientali imprevisti, quindi consigliamo di creare una funzione che utilizzi e verifichi l'intestazione firmata crittografica. La firma dell'intestazione non può essere falsificata e, una volta verificata, può essere utilizzata per restituire l'identificazione.

Crea il codice sorgente

  1. Utilizza un editor di testo per creare un file denominato main.py e incolla il codice seguente al suo interno:

    import sys
    
    from flask import Flask
    app = Flask(__name__)
    
    CERTS = None
    AUDIENCE = None
    
    def certs():
        """Returns a dictionary of current Google public key certificates for
        validating Google-signed JWTs. Since these change rarely, the result
        is cached on first request for faster subsequent responses.
        """
        import requests
    
        global CERTS
        if CERTS is None:
            response = requests.get(
                'https://www.gstatic.com/iap/verify/public_key'
            )
            CERTS = response.json()
        return CERTS
    
    def get_metadata(item_name):
        """Returns a string with the project metadata value for the item_name.
        See https://cloud.google.com/compute/docs/storing-retrieving-metadata for
        possible item_name values.
        """
        import requests
    
        endpoint = 'http://metadata.google.internal'
        path = '/computeMetadata/v1/project/'
        path += item_name
        response = requests.get(
            '{}{}'.format(endpoint, path),
            headers={'Metadata-Flavor': 'Google'}
        )
        metadata = response.text
        return metadata
    
    def audience():
        """Returns the audience value (the JWT 'aud' property) for the current
        running instance. Since this involves a metadata lookup, the result is
        cached when first requested for faster future responses.
        """
        global AUDIENCE
        if AUDIENCE is None:
            project_number = get_metadata('numeric-project-id')
            project_id = get_metadata('project-id')
            AUDIENCE = '/projects/{}/apps/{}'.format(
                project_number, project_id
            )
        return AUDIENCE
    
    def validate_assertion(assertion):
        """Checks that the JWT assertion is valid (properly signed, for the
        correct audience) and if so, returns strings for the requesting user's
        email and a persistent user ID. If not valid, returns None for each field.
        """
        from jose import jwt
    
        try:
            info = jwt.decode(
                assertion,
                certs(),
                algorithms=['ES256'],
                audience=audience()
                )
            return info['email'], info['sub']
        except Exception as e:
            print('Failed to validate assertion: {}'.format(e), file=sys.stderr)
            return None, None
    
    @app.route('/', methods=['GET'])
    def say_hello():
        from flask import request
    
        assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
        email, id = validate_assertion(assertion)
        page = "<h1>Hello {}</h1>".format(email)
        return page

    Questo file main.py è spiegato in dettaglio nella sezione Comprendere il codice più avanti in questo tutorial.

  2. Crea un altro file denominato requirements.txt e incolla quanto segue nel file:

    Flask==2.1.2
    cryptography==37.0.2
    python-jose[cryptography]==3.3.0
    requests==2.27.1

    Il file requirements.txt elenca eventuali librerie Python non standard necessarie per il caricamento della tua app in App Engine:

    • Flask è il framework web Python utilizzato per l'app.

    • cryptography è un modulo che fornisce funzioni crittografiche efficaci.

    • python-jose[cryptography] fornisce la funzione di controllo e decodifica JWT.

    • requests recupera i dati dai siti web.

  3. Crea un file denominato app.yaml e inserisci il testo riportato di seguito:

    runtime: python37

    Il file app.yaml indica ad App Engine quale ambiente linguistico richiede il tuo codice.

Nozioni di base sul codice

Questa sezione spiega come funziona il codice in main.py. Se vuoi semplicemente eseguire l'applicazione, puoi passare direttamente alla sezione Eseguire il deployment dell'app.

Il codice seguente è presente nel file main.py. Quando l'app riceve una richiesta HTTP GET alla home page, il framework Flask richiama la funzione say_hello:

@app.route('/', methods=['GET'])
def say_hello():
    from flask import request

    assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
    email, id = validate_assertion(assertion)
    page = "<h1>Hello {}</h1>".format(email)
    return page

La funzione say_hello riceve il valore di intestazione dell'asserzione JWT che l'IAP ha aggiunto dalla richiesta in entrata e chiama una funzione per convalidare tale valore firmato crittografico. Il primo valore restituito (email) viene poi utilizzato in una pagina web minima che crea e restituisce.

def validate_assertion(assertion):
    """Checks that the JWT assertion is valid (properly signed, for the
    correct audience) and if so, returns strings for the requesting user's
    email and a persistent user ID. If not valid, returns None for each field.
    """
    from jose import jwt

    try:
        info = jwt.decode(
            assertion,
            certs(),
            algorithms=['ES256'],
            audience=audience()
            )
        return info['email'], info['sub']
    except Exception as e:
        print('Failed to validate assertion: {}'.format(e), file=sys.stderr)
        return None, None

La funzione validate_assertion utilizza la funzione jwt.decode della libreria di terze parti jose per verificare che l'asserzione sia stata firmata correttamente e per estrarre le informazioni sul payload dall'asserzione. Tali informazioni sono l'indirizzo email dell'utente autenticato e un ID univoco permanente per l'utente. Se l'asserzione non può essere decodificata, questa funzione restituisce None per ciascuno di questi valori e stampa un messaggio per registrare l'errore.

Per convalidare un'asserzione JWT è necessario conoscere i certificati di chiave pubblica dell'entità che ha firmato l'asserzione (in questo caso, Google) e il pubblico a cui è destinata. Per un'app App Engine, il pubblico è una stringa contenente informazioni di identificazione del progetto Google Cloud. Questa funzione riceve i certificati e la stringa di pubblico dalle funzioni che lo precedono.

def audience():
    """Returns the audience value (the JWT 'aud' property) for the current
    running instance. Since this involves a metadata lookup, the result is
    cached when first requested for faster future responses.
    """
    global AUDIENCE
    if AUDIENCE is None:
        project_number = get_metadata('numeric-project-id')
        project_id = get_metadata('project-id')
        AUDIENCE = '/projects/{}/apps/{}'.format(
            project_number, project_id
        )
    return AUDIENCE

Puoi cercare l'ID numerico e il nome del progetto Google Cloud e inserirli personalmente nel codice sorgente, ma la funzione audience esegue questa operazione inviando una query al servizio di metadati standard reso disponibile in ogni app App Engine. Il servizio di metadati è esterno al codice dell'app, pertanto il risultato viene salvato in una variabile globale che viene restituita senza dover cercare i metadati nelle chiamate successive.

def get_metadata(item_name):
    """Returns a string with the project metadata value for the item_name.
    See https://cloud.google.com/compute/docs/storing-retrieving-metadata for
    possible item_name values.
    """
    import requests

    endpoint = 'http://metadata.google.internal'
    path = '/computeMetadata/v1/project/'
    path += item_name
    response = requests.get(
        '{}{}'.format(endpoint, path),
        headers={'Metadata-Flavor': 'Google'}
    )
    metadata = response.text
    return metadata

Il servizio di metadati App Engine (e i servizi di metadati simili per altri servizi di computing di Google Cloud) sembra un sito web e viene eseguito in base a query su tali query. Tuttavia, non è un sito esterno, ma una funzionalità interna che restituisce le informazioni richieste sull'app in esecuzione, quindi è sicuro utilizzare http invece delle richieste https. Viene utilizzato per ottenere gli identificatori Google Cloud correnti necessari a definire il pubblico di destinazione dell'asserzione JWT.

def certs():
    """Returns a dictionary of current Google public key certificates for
    validating Google-signed JWTs. Since these change rarely, the result
    is cached on first request for faster subsequent responses.
    """
    import requests

    global CERTS
    if CERTS is None:
        response = requests.get(
            'https://www.gstatic.com/iap/verify/public_key'
        )
        CERTS = response.json()
    return CERTS

La verifica di una firma digitale richiede il certificato di chiave pubblica del firmatario. Google fornisce un sito web che restituisce tutti i certificati di chiave pubblica attualmente utilizzati. Questi risultati vengono memorizzati nella cache nel caso in cui siano nuovamente necessari nella stessa istanza dell'app.

Deployment dell'app

Ora puoi eseguire il deployment dell'app e abilitare IAP per richiedere agli utenti di autenticarsi prima che possano accedere all'app.

  1. Nella finestra del terminale, vai alla directory contenente il file app.yaml ed esegui il deployment dell'app in App Engine:

    gcloud app deploy
    
  2. Quando richiesto, seleziona un'area geografica nelle vicinanze.

  3. Quando ti viene chiesto se vuoi continuare l'operazione di deployment, inserisci Y.

    Entro pochi minuti, la tua app viene pubblicata su Internet.

  4. Visualizza l'app:

    gcloud app browse
    

    Nell'output, copia web-site-url, l'indirizzo web dell'app.

  5. In una finestra del browser, incolla web-site-url per aprire l'applicazione.

    Non viene visualizzata alcuna email perché non stai ancora utilizzando IAP, quindi non vengono inviate informazioni utente all'app.

Abilita IAP

Ora che esiste un'istanza App Engine, puoi proteggerla con gli IAP:

  1. In Google Cloud Console, vai alla pagina Identity-Aware Proxy.

    Vai alla pagina Identity-Aware Proxy

  2. Poiché è la prima volta che attivi un'opzione di autenticazione per questo progetto, verrà visualizzato un messaggio che ti chiede di configurare la schermata per il consenso OAuth prima di poter utilizzare IAP.

    Fai clic su Configura la schermata di consenso.

  3. Nella scheda Schermata Consenso OAuth della pagina Credenziali, completa i seguenti campi:

    • Se il tuo account fa parte di un'organizzazione Google Workspace, seleziona Esterno e fai clic su Crea. Per iniziare, l'app sarà disponibile soltanto per gli utenti che consenti esplicitamente.

    • Nel campo Nome applicazione, inserisci IAP Example.

    • Inserisci il tuo indirizzo email nel campo Email di assistenza.

    • Nel campo Dominio autorizzato, inserisci la parte del nome host dell'URL dell'app, ad esempio iap-example-999999.uc.r.appspot.com. Dopo aver inserito il nome host nel campo, premi il tasto Enter.

    • Nel campo Link della home page dell'applicazione inserisci l'URL dell'app, ad esempio https://iap-example-999999.uc.r.appspot.com/.

    • Nel campo Applicazione delle norme sulla privacy dell'applicazione, utilizza lo stesso URL del link alla home page ai fini di test.

  4. Fai clic su Salva. Se ti viene chiesto di creare le credenziali, puoi chiudere la finestra.

  5. In Cloud Console, vai alla pagina Identity-Aware Proxy.

    Vai alla pagina Identity-Aware Proxy

  6. Per aggiornare la pagina, fai clic su Aggiorna . La pagina mostra un elenco di risorse che puoi proteggere.

  7. Nella colonna IAP, fai clic per attivare IAP per l'app.

  8. Nel browser, vai di nuovo a web-site-url.

  9. Al posto della pagina web, è disponibile una schermata di accesso per l'autenticazione. Quando esegui l'accesso, ti viene negato l'accesso perché IAP non dispone di un elenco di utenti da consentire all'app.

Aggiungere utenti autorizzati all'app

  1. In Cloud Console, vai alla pagina Identity-Aware Proxy.

    Vai alla pagina Identity-Aware Proxy

  2. Seleziona la casella di controllo per l'app App Engine e fai clic su Aggiungi entità.

  3. Inserisci allAuthenticatedUsers, quindi seleziona il ruolo Utente di app web protetto da IAP/IAP.

  4. Fai clic su Salva.

Ora qualsiasi utente che Google può autenticare può accedere all'applicazione. Se vuoi, puoi limitare ulteriormente l'accesso aggiungendo solo una o più persone o gruppi come entità:

  • Qualsiasi indirizzo email Gmail o Google Workspace

  • Un indirizzo email di Google Gruppi

  • Un nome di dominio Google Workspace

Accedi all'app

  1. Nel browser, vai a web-site-url.

  2. Per aggiornare la pagina, fai clic su Aggiorna .

  3. Nella schermata di accesso, accedi con le tue credenziali Google.

    Nella pagina viene visualizzata una pagina "Ciao user-email-address" con il tuo indirizzo email.

    Se continua a visualizzare la stessa pagina precedente, potrebbe esserci un problema con il browser che non aggiorna completamente le nuove richieste quando hai attivato gli IAP. Chiudi e riapri tutte le finestre del browser, quindi riprova.

Concetti di autenticazione

Esistono diversi modi per autenticare un'app e limitare l'accesso solo agli utenti autorizzati. I metodi di autenticazione più comuni, con un minore livello di impegno per l'app, sono elencati nelle sezioni seguenti.

Opzione Vantaggi Svantaggi
Autenticazione app
  • L'app può essere eseguita su qualsiasi piattaforma, con o senza una connessione a Internet
  • Gli utenti non devono utilizzare altri servizi per gestire l'autenticazione
  • L'app deve gestire le credenziali dell'utente in modo sicuro, proteggerla dalle informative
  • L'app deve conservare i dati delle sessioni per gli utenti che hanno eseguito l'accesso
  • L'app deve fornire la registrazione utente, le modifiche alle password, il recupero della password
OAuth2
  • L'app può essere eseguita su qualsiasi piattaforma connessa a Internet, inclusa una workstation per sviluppatori
  • L'app non richiede la registrazione utente, le modifiche alle password o funzioni di recupero delle password.
  • Il rischio di divulgazione di informazioni utente è delegato a un altro servizio
  • Nuove misure di sicurezza dell'accesso gestite al di fuori dell'app
  • Gli utenti devono registrarsi con il servizio di identità
  • L'app deve conservare i dati delle sessioni per gli utenti che hanno eseguito l'accesso
IAP
  • L'app non deve contenere codice per gestire gli utenti, l'autenticazione o lo stato della sessione
  • L'app non ha credenziali utente che potrebbero essere violate
  • L'app può essere eseguita solo su piattaforme supportate dal servizio. In particolare, alcuni servizi Google Cloud che supportano IAP, come App Engine.

Autenticazione gestita dall'app

Questo metodo consente all'app di gestire autonomamente ogni aspetto dell'autenticazione utente. L'app deve mantenere il proprio database di credenziali utente e gestire le sessioni utente e deve fornire funzioni per gestire account utente e password, verificare le credenziali utente, nonché emettere, controllare e aggiornare le sessioni utente con ogni accesso autenticato. Il seguente diagramma illustra il metodo di autenticazione gestito dall'app.

Flusso gestito dall'applicazione

Come mostrato nel diagramma, dopo che l'utente ha eseguito l'accesso, l'app crea e mantiene le informazioni sulla sessione dell'utente. Quando l'utente invia una richiesta all'app, la richiesta deve includere le informazioni sulla sessione che l'app è responsabile di verificare.

Il vantaggio principale di questo approccio è che è indipendente e sotto il controllo dell'app. L'app non deve nemmeno essere disponibile su Internet. Lo svantaggio principale è che l'app è ora responsabile di fornire tutte le funzionalità di gestione degli account e di proteggere tutti i dati sensibili delle credenziali.

Autenticazione esterna con OAuth2

Un'alternativa valida alla gestione di tutto ciò che si trova all'interno dell'app è l'utilizzo di un servizio di identità esterno, come Google, che gestisca tutte le informazioni e le funzionalità dell'account utente ed è responsabile della protezione delle credenziali sensibili. Quando un utente tenta di accedere all'app, la richiesta viene reindirizzata al servizio di identità, che autentica l'utente e quindi reindirizza la richiesta all'app con le informazioni di autenticazione necessarie. Per ulteriori informazioni, consulta la sezione Autenticare come utente finale.

Il seguente diagramma illustra l'autenticazione esterna con il metodo OAuth2.

Flusso OAuth2

Il flusso nel diagramma inizia quando l'utente invia una richiesta di accesso all'app. Invece di rispondere direttamente, l'app reindirizza il browser dell'utente alla piattaforma di identità di Google, che mostra una pagina in cui accedere a Google. Dopo aver eseguito correttamente l'accesso, il browser dell'utente viene reindirizzato all'app. La richiesta include informazioni che l'app può utilizzare per cercare informazioni sull'utente ora autenticato e che l'app risponde all'utente.

Questo metodo presenta molti vantaggi per l'app. Delega tutte le funzionalità e i rischi della gestione degli account al servizio esterno, che può migliorare l'accesso e la sicurezza dell'account senza che l'app debba essere modificata. Tuttavia, come mostrato nel diagramma precedente, l'app deve avere accesso a Internet per poter utilizzare questo metodo. L'app è anche responsabile della gestione delle sessioni dopo l'autenticazione dell'utente.

Identity-Aware Proxy

Il terzo approccio, trattato in questo tutorial, è di utilizzare IAP per gestire tutte le attività di autenticazione e gestione delle sessioni con eventuali modifiche all'app. IAP intercetta tutte le richieste web che riguardano l'app, blocca tutte le app che non sono state autenticate e le altre vengono trasferite con i dati sull'identità utente aggiunti a ogni richiesta.

La gestione delle richieste è mostrata nel diagramma seguente.

Flusso IAP

Le richieste degli utenti vengono intercettate da IAP, che blocca le richieste non autenticate. Le richieste autenticate vengono trasmesse all'app, a condizione che l'utente autenticato sia presente nell'elenco degli utenti autorizzati. Alle richieste trasmesse tramite IAP vengono aggiunte intestazioni che identificano l'utente che ha effettuato la richiesta.

L'app non deve più gestire le informazioni di sessioni o account utente. Qualsiasi operazione che richieda la conoscenza di un identificatore univoco per l'utente può recuperarla direttamente da ogni richiesta web in entrata. Tuttavia, può essere utilizzato solo per i servizi di computing che supportano IAP, come App Engine e i bilanciatori del carico. Non puoi utilizzare IAP su una macchina di sviluppo locale.

Esegui la pulizia

Per evitare che al tuo Account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

  1. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.