Esta página descreve como proteger a sua app com cabeçalhos de IAP assinados. Quando configurado, o Identity-Aware Proxy (IAP) usa tokens Web JSON (JWT) para garantir que um pedido à sua app está autorizado. Isto protege a sua app dos seguintes tipos de riscos:
- A IAP está desativada acidentalmente;
- Firewalls configuradas incorretamente;
- Aceda a partir do projeto.
Para proteger corretamente a sua app, tem de usar cabeçalhos assinados para todos os tipos de apps.
Em alternativa, se tiver uma app do ambiente padrão do App Engine, pode usar a API Users.
Tenha em atenção que as verificações de estado do Compute Engine e do GKE não incluem cabeçalhos JWT e o IAP não processa verificações de estado. Se a verificação de estado devolver erros de acesso, certifique-se de que a configurou corretamente na consola Google Cloud e que a validação do cabeçalho JWT permite o caminho da verificação de estado. Para mais informações, consulte o artigo Crie uma exceção de verificação de estado.
Antes de começar
Para proteger a sua app com cabeçalhos assinados, precisa do seguinte:
- Uma aplicação à qual quer que os utilizadores se liguem.
- Uma biblioteca JWT de terceiros para o seu idioma que suporte o algoritmo
ES256
.
Proteger a sua app com cabeçalhos de IAP
Para proteger a sua app com o JWT de IAP, valide o cabeçalho, a carga útil e a assinatura do JWT. O JWT está no cabeçalho do pedido HTTP
x-goog-iap-jwt-assertion
. Se um atacante contornar a IAP, pode falsificar os cabeçalhos de identidade não assinados da IAPx-goog-authenticated-user-{email,id}
. O JWT da IAP oferece uma alternativa mais segura.
Os cabeçalhos assinados oferecem segurança secundária caso alguém contorne o IAP. Tenha em atenção que, quando a IAP está ativada, remove os cabeçalhos x-goog-*
fornecidos pelo cliente quando o pedido passa pela infraestrutura de publicação da IAP.
Validar o cabeçalho JWT
Verifique se o cabeçalho do JWT está em conformidade com as seguintes restrições:
Reivindicações do cabeçalho JWT | ||
---|---|---|
alg |
Algoritmo | ES256 |
kid |
ID da chave |
Tem de corresponder a uma das chaves públicas indicadas no ficheiro de chaves de IAP, disponível em dois formatos diferentes:
https://www.gstatic.com/iap/verify/public_key
e
https://www.gstatic.com/iap/verify/public_key-jwk
|
Certifique-se de que o JWT foi assinado pela chave privada que corresponde à reivindicação kid
do token. Para tal, comece por obter a chave pública num dos seguintes locais:
https://www.gstatic.com/iap/verify/public_key
. Este URL contém um dicionário JSON que mapeia as reivindicaçõeskid
para os valores da chave pública.https://www.gstatic.com/iap/verify/public_key-jwk
. Este URL contém as chaves públicas da IAP no formato JWK.
Depois de ter a chave pública, use uma biblioteca JWT para validar a assinatura.
Validar a carga útil do JWT
Verifique se a carga útil do JWT está em conformidade com as seguintes restrições:
Reivindicações de payload JWT | ||
---|---|---|
exp |
Período de validade | Tem de ser no futuro. O tempo é medido em segundos desde a época UNIX. Aguarde 30 segundos para a distorção. A duração máxima de um token é de 10 minutos + 2 * skew. |
iat |
Hora de emissão | Tem de ser no passado. O tempo é medido em segundos desde a época UNIX. Aguarde 30 segundos para a distorção. |
aud |
Público-alvo |
Tem de ser uma string com os seguintes valores:
|
iss |
Emissor |
Tem de ser https://cloud.google.com/iap .
|
hd |
Domínio da conta |
Se uma conta pertencer a um domínio alojado, a reivindicação é fornecida para diferenciar o domínio ao qual a conta está associada.hd
|
google |
Reivindicação da Google |
Se um ou mais níveis de acesso
se aplicarem ao pedido, os respetivos nomes são armazenados no objeto JSON
da reivindicação, na chave access_levels , como uma matriz
de strings.google
Quando especifica uma política de dispositivo e a organização tem acesso aos dados do dispositivo, o |
Pode obter os valores da string aud
mencionada acima acedendo à
Google Cloud consola ou usar a ferramenta de linhas de comando gcloud.
Para obter valores de string aud
da Google Cloud consola, aceda às
definições do Identity-Aware Proxy
para o seu projeto, clique em Mais junto ao recurso do balanceador de carga e, de seguida,
selecione Público-alvo do JWT do cabeçalho assinado. A caixa de diálogo JWT de cabeçalho assinado apresentada mostra a reivindicação aud
para o recurso selecionado.
Se quiser usar a CLI gcloud
ferramenta de linhas de comando gcloud para obter os valores de string aud
, tem de saber
o ID do projeto. Pode encontrar o ID do projeto no cartão
Google Cloud consola
Informações do projeto e, em seguida, executar os comandos especificados abaixo para cada valor.
Número do projeto
Para obter o número do projeto através da ferramenta de linhas de comando gcloud, execute o seguinte comando:
gcloud projects describe PROJECT_ID
O comando devolve um resultado semelhante ao seguinte:
createTime: '2016-10-13T16:44:28.170Z' lifecycleState: ACTIVE name: project_name parent: id: '433637338589' type: organization projectId: PROJECT_ID projectNumber: 'PROJECT_NUMBER'
ID do serviço
Para obter o ID do serviço através da ferramenta de linhas de comando gcloud, execute o seguinte comando:
gcloud compute backend-services describe SERVICE_NAME --project=PROJECT_ID --global
O comando devolve um resultado semelhante ao seguinte:
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
Obter a identidade do utilizador
Se todas as validações acima forem bem-sucedidas, obtenha a identidade do utilizador. O payload do token de ID contém as seguintes informações do utilizador:
Identidade do utilizador da carga útil do token de ID | ||
---|---|---|
sub |
Assunto |
O identificador exclusivo e estável do utilizador. Use este valor em vez do cabeçalho x-goog-authenticated-user-id .
|
email |
Email do utilizador | Endereço de email do utilizador.
|
Segue-se um exemplo de código para proteger uma app com cabeçalhos de IAP assinados:
C#
Go
Java
Node.js
PHP
Python
Ruby
Testar o código de validação
Se visitar a sua app através dos parâmetros de consulta secure_token_test
, a IAP vai incluir um JWT inválido. Use esta opção para se certificar de que a lógica de validação de JWT está a processar todos os vários casos de falha e para ver como a sua app se comporta quando recebe um JWT inválido.
Criar uma exceção de verificação de funcionamento
Conforme mencionado anteriormente, as verificações de estado do Compute Engine e do GKE não usam cabeçalhos JWT, e o IAP não processa as verificações de estado. Tem de configurar a verificação de estado e a app para permitir o acesso à verificação de estado.
Configurar a verificação de funcionamento
Se ainda não tiver definido um caminho para a verificação de funcionamento, use a Google Cloud consola para definir um caminho não sensível para a verificação de funcionamento. Certifique-se de que este caminho não é partilhado por nenhum outro recurso.
- Aceda à página Google Cloud consola
Verificações de estado.
Aceda à página Verificações de saúde - Clique na verificação de estado que está a usar para a sua app e, de seguida, clique em Editar.
- Em Caminho do pedido, adicione um nome de caminho não sensível. Isto especifica o caminho do URL que o serviço usa quando envia pedidos de verificação do estado de funcionamento. Google Cloud
Se for omitido, o pedido de verificação de funcionamento é enviado para
/
. - Clique em Guardar.
Configurar a validação de JWT
No código que chama a rotina de validação JWT, adicione uma condição para publicar um estado HTTP 200 para o caminho do pedido de verificação do estado. Por exemplo:
if HttpRequest.path_info = '/HEALTH_CHECK_REQUEST_PATH' return HttpResponse(status=200) else VALIDATION_FUNCTION
JWTs para identidades externas
Se estiver a usar a IAP com identidades externas, a IAP continua a emitir um JWT assinado em cada pedido autenticado, tal como faz com as identidades Google. No entanto, existem algumas diferenças.
Informações do fornecedor
Quando usar identidades externas, a carga útil do JWT vai conter uma reivindicação com o nome gcip
. Esta reivindicação contém informações sobre o utilizador, como o respetivo
URL de email e foto, bem como quaisquer atributos adicionais específicos do fornecedor.
Segue-se um exemplo de um JWT para um utilizador que iniciou sessão com o 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"
}',
Os campos email
e sub
Se um utilizador foi autenticado pela Identity Platform, os campos email
e sub
do JWT vão ter o prefixo do emissor do token da Identity Platform e o ID do inquilino usado (se existir). Por exemplo:
"email": "securetoken.google.com/PROJECT-ID/TENANT-ID:demo_user@gmail.com", "sub": "securetoken.google.com/PROJECT-ID/TENANT-ID:gZG0yELPypZElTmAT9I55prjHg63"
Controlar o acesso com o sign_in_attributes
A IAM não é suportada para utilização com identidades externas, mas pode usar reivindicações incorporadas no campo sign_in_attributes
para controlar o acesso. Por exemplo, considere um utilizador com sessão iniciada através de um fornecedor 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"
}
Pode adicionar lógica à sua aplicação semelhante ao código abaixo para restringir o acesso a utilizadores com uma função válida:
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.
}
Pode aceder a atributos de utilizador adicionais dos fornecedores SAML e OIDC da Identity Platform através da reivindicação gcipClaims.gcip.firebase.sign_in_attributes
aninhada.
Limitações de tamanho das reivindicações do IdP
Depois de um utilizador iniciar sessão com a Identity Platform, os atributos do utilizador adicionais são propagados para a carga útil do token de ID da Identity Platform sem estado, que é transmitida de forma segura para o IAP. Em seguida, o IAP emite o seu próprio cookie opaco sem estado, que também contém as mesmas reivindicações. O IAP gera o cabeçalho JWT assinado com base no conteúdo do cookie.
Como resultado, se uma sessão for iniciada com um grande número de reivindicações, pode exceder o tamanho máximo permitido de cookies, que é normalmente de ~4 KB na maioria dos navegadores. Esta ação faz com que a operação de início de sessão falhe.
Deve garantir que apenas as reivindicações necessárias são propagadas nos atributos SAML ou OIDC do IdP. Outra opção é usar as funções de bloqueio para filtrar as reivindicações que não são necessárias para a verificação de autorização.
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,
};
}
});