Sécuriser votre application avec des en-têtes signés

Cette page explique comment sécuriser votre application avec des en-têtes IAP signés. Une fois configuré, Identity-Aware Proxy (IAP) utilise des jetons Web JSON (JWT, JSON Web Token) pour s'assurer qu'une requête envoyée à votre application est autorisée. Cela protège votre application contre les risques suivants :

  • Désactivation accidentelle du service IAP
  • Pare-feu mal configurés
  • Accès non autorisé depuis le projet

Pour sécuriser votre application, vous devez utiliser des en-têtes signés pour tous les types d'applications.

Si vous disposez d'une application d'environnement standard App Engine, vous pouvez également utiliser l'API Users.

Les vérifications d'état de Compute Engine et de GKE n'incluent pas les en-têtes JWT, et IAP ne traite pas les vérifications d'état. Si votre vérification de l'état renvoie des erreurs d'accès, assurez-vous que vous l'vérification de l'étatavez correctement configurée dans la Google Cloud console et que votre validation d'en-tête JWT autorise le chemin de la vérification de l'état. Pour en savoir plus, consultez la section Créer une exception de vérification de l'étatétat.

Avant de commencer

Pour sécuriser votre application avec des en-têtes signés, vous avez besoin des éléments suivants :

Sécuriser votre application avec des en-têtes IAP

Pour sécuriser votre application avec le JWT IAP, validez l'en-tête, la charge utile et la signature de celui-ci. Le JWT se trouve dans l'en-tête de requête HTTP x-goog-iap-jwt-assertion. En contournant IAP, un pirate informatique peut falsifier les en-têtes d'identité IAP non signés : x-goog-authenticated-user-{email,id}. Le JWT IAP constitue une alternative plus sécurisée.

Les en-têtes signés offrent une sécurité secondaire dans le cas d'un éventuel contournement d'IAP. Lorsque IAP est activé, il retire les en-têtes x-goog-* fournis par le client quand la requête passe par l'infrastructure de diffusion du service.

Valider l'en-tête JWT

Assurez-vous que l'en-tête JWT respecte les contraintes suivantes :

Revendications d'en-tête JWT
alg Algorithme ES256
kid ID de clé Doit correspondre à l'une des clés publiques répertoriées dans le fichier de clé IAP, disponible dans deux formats différents : https://www.gstatic.com/iap/verify/public_key et https://www.gstatic.com/iap/verify/public_key-jwk

Assurez-vous que le JWT a été signé par la clé privée correspondant à la revendication kid du jeton. Commencez par récupérer la clé publique à l'un des deux emplacements suivants :

  • https://www.gstatic.com/iap/verify/public_key. Cette URL contient un dictionnaire JSON qui mappe les revendications kid avec les valeurs de la clé publique.
  • https://www.gstatic.com/iap/verify/public_key-jwk. Cette URL contient les clés publiques IAP au format JWK.

Une fois la clé publique récupérée, validez la signature à l'aide d'une bibliothèque JWT.

IAP alterne régulièrement ses clés publiques. Pour vous assurer de toujours pouvoir valider les JWT, consultez la section Automatiser la mise en cache des clés publiques.

Valider la charge utile JWT

Assurez-vous que la charge utile JWT respecte les contraintes suivantes :

Revendications de charge utile JWT
exp Date/Heure d'expiration Il doit s'agir d'une date future. Le temps est mesuré en secondes depuis l'époque UNIX. Prévoyez 30 secondes de décalage. La durée de vie maximale d'un jeton est de 10 minutes + 2 * le décalage.
iat Date/Heure d'émission Il doit s'agir d'une date antérieure. Le temps est mesuré en secondes depuis l'époque UNIX. Prévoyez 30 secondes de décalage.
aud Cible Doit être une chaîne avec les valeurs suivantes :
  • App Engine: /projects/PROJECT_NUMBER/apps/PROJECT_ID
  • Compute Engine et GKE: /projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID
  • Cloud Run: /projects/PROJECT_NUMBER/locations/REGION/services/SERVICE_NAME
iss Émetteur Doit être https://cloud.google.com/iap.
hd Domaine du compte Si un compte appartient à un domaine hébergé, la hd revendication est fournie afin de différencier le domaine auquel le compte est associé.
google Revendication Google Si un ou plusieurs niveaux d'accès s'appliquent à la requête, leur nom est stocké sous forme de tableau de chaînes dans l'objet JSON de la revendication google , sous la clé access_levels.

Lorsque vous spécifiez une règle d'appareil et que l'organisation a accès aux données de l'appareil, le DeviceId est également stocké dans l'objet JSON. Notez qu'une requête adressée à une autre organisation peut ne pas être autorisée à afficher les données de l'appareil.

Vous pouvez obtenir les valeurs de la chaîne aud mentionnée ci-dessus en accédant à la Google Cloud console, ou vous pouvez utiliser l'outil de ligne de commande gcloud.

Pour obtenir les valeurs de chaîne aud depuis la Google Cloud console, accédez aux paramètres Identity-Aware Proxy de votre projet, cliquez sur Plus à côté de la ressource de l'équilibreur de charge, puis sélectionnez Signed Header JWT Audience . La boîte de dialogue Jeton JWT avec en-tête signé qui apparaît affiche la revendication aud pour la ressource sélectionnée.

Pour obtenir les valeurs de la chaîne aud à l'aide de l'outil de ligne de commande gcloud CLI gcloud, vous devez connaître l'ID du projet. Vous trouverez l'ID du projet sur la fiche Google Cloud console Informations sur le projet, puis exécutez les commandes spécifiées pour chaque valeur.

Numéro du projet

Pour obtenir votre numéro de projet à l'aide de l'outil de ligne de commande gcloud, exécutez la commande suivante :

gcloud projects describe PROJECT_ID

La commande renvoie un résultat semblable à celui-ci :

createTime: '2016-10-13T16:44:28.170Z'
lifecycleState: ACTIVE
name: project_name
parent:
  id: '433637338589'
  type: organization
projectId: PROJECT_ID
projectNumber: 'PROJECT_NUMBER'

ID du service

Pour obtenir votre ID de service à l'aide de l'outil de ligne de commande gcloud, exécutez la commande suivante :

gcloud compute backend-services describe SERVICE_NAME --project=PROJECT_ID --global

La commande renvoie un résultat semblable à celui-ci :

affinityCookieTtlSec: 0
backends:
- balancingMode: UTILIZATION
  capacityScaler: 1.0
  group: https://www.googleapis.com/compute/v1/projects/project_name/regions/us-central1/instanceGroups/my-group
connectionDraining:
  drainingTimeoutSec: 0
creationTimestamp: '2017-04-03T14:01:35.687-07:00'
description: ''
enableCDN: false
fingerprint: zaOnO4k56Cw=
healthChecks:
- https://www.googleapis.com/compute/v1/projects/project_name/global/httpsHealthChecks/my-hc
id: 'SERVICE_ID'
kind: compute#backendService
loadBalancingScheme: EXTERNAL
name: my-service
port: 8443
portName: https
protocol: HTTPS
selfLink: https://www.googleapis.com/compute/v1/projects/project_name/global/backendServices/my-service
sessionAffinity: NONE
timeoutSec: 3610

Récupérer l'identité de l'utilisateur

Si toutes les validations précédentes ont réussi, récupérez l'identité de l'utilisateur. La charge utile du jeton d'ID contient les informations utilisateur suivantes :

Identité de l'utilisateur de la charge utile du jeton d'ID
sub Objet Identifiant unique et stable pour l'utilisateur. Utilisez cette valeur à la place de l'en-tête x-goog-authenticated-user-id.
email Adresse e-mail de l'utilisateur Adresse e-mail de l'utilisateur.
  • Utilisez cette valeur à la place de l' x-goog-authenticated-user-email en-tête.
  • Contrairement à cet en-tête et à la revendication sub, cette valeur ne comporte pas de préfixe d'espace de noms.

Vous trouverez ci-dessous un exemple de code permettant de sécuriser une application avec des en-têtes IAP signés :

C#


using Google.Apis.Auth;
using Google.Apis.Auth.OAuth2;
using System;
using System.Threading;
using System.Threading.Tasks;

public class IAPTokenVerification
{
    /// <summary>
    /// Verifies a signed jwt token and returns its payload.
    /// </summary>
    /// <param name="signedJwt">The token to verify.</param>
    /// <param name="expectedAudience">The audience that the token should be meant for.
    /// Validation will fail if that's not the case.</param>
    /// <param name="cancellationToken">The cancellation token to propagate cancellation requests.</param>
    /// <returns>A task that when completed will have as its result the payload of the verified token.</returns>
    /// <exception cref="InvalidJwtException">If verification failed. The message of the exception will contain
    /// information as to why the token failed.</exception>
    public async Task<JsonWebSignature.Payload> VerifyTokenAsync(
        string