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. Cette démarche protège votre application contre les types de risques suivants :
- Désactivation accidentelle du service IAP
- Pare-feu mal configurés
- Accès depuis le projet
Afin de sécuriser correctement votre application, vous devez vous servir d'en-têtes signés qui conviennent à tous les types d'application.
Si vous disposez d'une application d'environnement standard App Engine, vous pouvez également utiliser l'API Users.
Sachez que les vérifications d'état de Compute Engine et de GKE n'incluent pas les en-têtes JWT, et qu'IAP ne gère pas ces vérifications. Si votre vérification de l'état renvoie des erreurs d'accès, assurez-vous que vous l'avez correctement configurée dans la console Google Cloud et que votre validation d'en-tête JWT autorise le chemin de la vérification de l'état d'état. Pour en savoir plus, consultez la section Créer une exception de vérification d'état.
Avant de commencer
Pour sécuriser votre application avec des en-têtes signés, vous avez besoin des éléments suivants :
- Une application à laquelle vos utilisateurs se connecteront
- Une bibliothèque JWT tierce correspondant à votre langage et compatible avec l'algorithme
ES256
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. Sachez que lorsqu'il est activé, IAP 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. Pour ce faire, récupérez d'abord 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 revendicationskid
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.
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 comportant les valeurs suivantes :
|
iss |
Émetteur | Doit être https://cloud.google.com/iap . |
hd |
Domaine du compte | Si un compte appartient à un domaine hébergé, la revendication hd 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 stratégie d'appareil et que l'organisation a accès aux données de l'appareil, |
Vous pouvez obtenir les valeurs de la chaîne aud
mentionnée ci-dessus en accédant à la console Google Cloud, ou vous pouvez utiliser l'outil de ligne de commande gcloud.
Pour obtenir les valeurs de la chaîne aud
depuis la console Google Cloud, 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 (Audience de jeton JWT avec en-tête signé). 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 de la gcloud CLI, vous devez connaître l'ID du projet. Vous le trouverez dans la fiche Informations sur le projet de la console Google Cloud. Exécutez ensuite les commandes indiquées ci-dessous 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 vérifications ci-dessus réussissent, vous pouvez récupérer 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.
|
Vous trouverez ci-dessous un exemple de code permettant de sécuriser une application avec des en-têtes IAP signés :
C#
Go
Java
Node.js
PHP
Python
Ruby
Tester le code de validation
Si vous accédez à votre application à l'aide des paramètres de requête secure_token_test
, IAP inclut un JWT non valide. Utilisez-le pour vous assurer que la logique de validation des JWT gère tous les cas d'échec et pour connaître le comportement de votre application lorsqu'elle reçoit un JWT non valide.
Créer une exception de vérification d'état
Comme mentionné précédemment, les vérifications d'état de Compute Engine et de GKE n'utilisent pas les en-têtes JWT, et IAP ne gère pas ces vérifications. Vous devez configurer votre vérification d'état et votre application pour autoriser l'accès à ces vérifications.
Configurer la vérification d'état
Si vous n'avez pas encore défini de chemin pour votre vérification de l'état d'état, attribuez-lui un chemin non sensible à l'aide de la console Google Cloud. Veillez à ce qu'aucune autre ressource ne partage ce chemin.
- Accédez à la page Vérifications d'état de la console Google Cloud.
Accéder à la page "Vérifications d'état" - Cliquez sur la vérification d'état que vous utilisez pour votre application, puis sur Modifier.
- Dans le champ Chemin de requête, ajoutez un nom de chemin non sensible. Cela permet de spécifier le chemin d'URL que Google Cloud utilise lors de l'envoi de requêtes de vérification d'état.
En cas d'omission, la requête de vérification d'état est envoyée à
/
. - Cliquez sur Save.
Configurer la validation JWT
Dans votre code qui appelle le processus de validation JWT, ajoutez une condition pour renvoyer un code 200 à votre chemin de vérification d'état. Exemple :
if HttpRequest.path_info = '/HEALTH_CHECK_REQUEST_PATH' return HttpResponse(status=200) else VALIDATION_FUNCTION
Les JWT pour les identités externes
Si vous utilisez IAP avec des identités externes, IAP émet tout de même un JWT signé à chaque requête authentifiée, tout comme avec les identités Google. Il existe toutefois quelques différences.
Informations sur le fournisseur
Lorsque vous utilisez des identités externes, la charge utile JWT contient une revendication nommée gcip
. Cette revendication contient des informations sur l'utilisateur, telles que son adresse e-mail et l'URL de sa photo, ainsi que tous les autres attributs propres au fournisseur.
Voici un exemple de JWT pour un utilisateur qui s'est connecté avec Facebook :
"gcip": '{
"auth_time": 1553219869,
"email": "facebook_user@gmail.com",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"facebook_user@gmail.com"
],
"facebook.com": [
"1234567890"
]
},
"sign_in_provider": "facebook.com",
},
"name": "Facebook User",
"picture: "https://graph.facebook.com/1234567890/picture",
"sub": "gZG0yELPypZElTmAT9I55prjHg63"
}',
Champs email
et sub
Si un utilisateur a été authentifié par Identity Platform, les champs email
et sub
du JWT sont préfixés avec l'émetteur de jeton Identity Platform et l'ID de locataire utilisé (le cas échéant). Exemple :
"email": "securetoken.google.com/PROJECT-ID/TENANT-ID:demo_user@gmail.com", "sub": "securetoken.google.com/PROJECT-ID/TENANT-ID:gZG0yELPypZElTmAT9I55prjHg63"
Contrôler l'accès avec sign_in_attributes
IAM n'est pas compatible avec les identités externes, mais vous pouvez contrôler l'accès à l'aide de revendications intégrées dans le champ sign_in_attributes
à la place. Prenons l'exemple d'un utilisateur qui s'est connecté avec un fournisseur SAML :
{
"aud": "/projects/project_number/apps/my_project_id",
"gcip": '{
"auth_time": 1553219869,
"email": "demo_user@gmail.com",
"email_verified": true,
"firebase": {
"identities": {
"email": [
"demo_user@gmail.com"
],
"saml.myProvider": [
"demo_user@gmail.com"
]
},
"sign_in_attributes": {
"firstname": "John",
"group": "test group",
"role": "admin",
"lastname": "Doe"
},
"sign_in_provider": "saml.myProvider",
"tenant": "my_tenant_id"
},
"sub": "gZG0yELPypZElTmAT9I55prjHg63"
}',
"email": "securetoken.google.com/my_project_id/my_tenant_id:demo_user@gmail.com",
"exp": 1553220470,
"iat": 1553219870,
"iss": "https://cloud.google.com/iap",
"sub": "securetoken.google.com/my_project_id/my_tenant_id:gZG0yELPypZElTmAT9I55prjHg63"
}
Vous pouvez ajouter à votre application une logique semblable au code ci-dessous pour restreindre l'accès aux utilisateurs disposant d'un rôle valide :
const gcipClaims = JSON.parse(decodedIapJwtClaims.gcip);
if (gcipClaims &&
gcipClaims.firebase &&
gcipClaims.firebase.sign_in_attributes &&
gcipClaims.firebase.sign_in_attribute.role === 'admin') {
// Allow access to admin restricted resource.
} else {
// Block access.
}
Vous pouvez accéder à d'autres attributs utilisateur des fournisseurs SAML et OIDC Identity Platform à l'aide de la revendication imbriquée gcipClaims.gcip.firebase.sign_in_attributes
.
Limites de taille des revendications d'IDP
Une fois qu'un utilisateur se connecte avec Identity Platform, les attributs utilisateur supplémentaires sont propagés à la charge utile du jeton d'ID Identity Platform sans état, qui est transmise de manière sécurisée à l'IAP. L'IAP émet ensuite son propre cookie opaque sans état, qui contient également les mêmes revendications. IAP génère l'en-tête JWT signé en fonction du contenu du cookie.
Par conséquent, si une session est lancée avec un grand nombre de revendications, elle peut dépasser la taille maximale autorisée des cookies, qui est généralement d'environ 4 ko dans la plupart des navigateurs. L'opération de connexion échoue.
Vous devez vous assurer que seules les revendications nécessaires sont propagées dans les attributs SAML ou OIDC de l'IdP. Vous pouvez également utiliser des fonctions de blocage pour filtrer les revendications qui ne sont pas requises pour la vérification de l'autorisation.
const gcipCloudFunctions = require('gcip-cloud-functions');
const authFunctions = new gcipCloudFunctions.Auth().functions();
// This function runs before any sign-in operation.
exports.beforeSignIn = authFunctions.beforeSignInHandler((user, context) => {
if (context.credential &&
context.credential.providerId === 'saml.my-provider') {
// Get the original claims.
const claims = context.credential.claims;
// Define this function to filter out the unnecessary claims.
claims.groups = keepNeededClaims(claims.groups);
// Return only the needed claims. The claims will be propagated to the token
// payload.
return {
sessionClaims: claims,
};
}
});