Nesta página, você aprenderá como proteger seu app com cabeçalhos assinados pelo IAP. Quando configurado, o Identity-Aware Proxy (IAP) usa JSON Web Tokens (JWT) para garantir que qualquer solicitação direcionada ao app seja autorizada. Isso protege o aplicativo dos seguintes tipos de riscos:
- IAP acidentalmente desativado
- Firewalls mal configurados
- Acesso a partir do projeto
Para proteger adequadamente seu aplicativo, você precisa usar cabeçalhos assinados para todos os tipos de aplicativos.
Como alternativa, se você tiver um aplicativo do ambiente padrão do App Engine, a API Users poderá ser usada.
As verificações de integridade do Compute Engine e do GKE não incluem os cabeçalhos JWT. Além disso, o IAP não processa verificações de integridade. Confirme se você a tem se a verificação de integridade retornar erros de acesso configurado corretamente no console do Google Cloud e que o cabeçalho JWT a validação permite o caminho de verificação de integridade. Para mais informações, consulte Como criar uma exceção de verificação de integridade.
Antes de começar
Para proteger o aplicativo com cabeçalhos assinados, você precisará do seguinte:
- Um aplicativo ao qual você quer que os usuários se conectem
- Uma biblioteca de JWT de terceiros para sua linguagem (em inglês) que seja compatível com o algoritmo
ES256
Como proteger seu app com cabeçalhos do IAP
Para proteger seu app com o JWT do IAP, verifique o cabeçalho, o payload e a assinatura do JWT. O JWT está no cabeçalho de solicitação HTTP x-goog-iap-jwt-assertion
. Se um invasor evadir o IAP, ele poderá forjar os cabeçalhos de identidade x-goog-authenticated-user-{email,id}
não assinados pelo IAP. O JWT do IAP é uma alternativa mais segura.
Os cabeçalhos assinados proporcionam uma camada secundária de segurança caso alguém desvie do IAP. Quando ativado, o IAP remove os cabeçalhos x-goog-*
fornecidos pelo cliente sempre que as solicitações passam pela infraestrutura de exibição desse recurso.
Como verificar o cabeçalho do JWT
Verifique se o cabeçalho do JWT está em conformidade com as seguintes restrições:
Declarações do cabeçalho JWT | ||
---|---|---|
alg |
Algoritmo | ES256 |
kid |
ID da chave | Precisa corresponder a uma das chaves públicas listadas no arquivo de chave do 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 corresponda à declaração kid
do token. Para fazer isso, primeiro consiga a chave pública de um destes dois locais:
https://www.gstatic.com/iap/verify/public_key
: este URL contém um dicionário JSON que mapeia as declaraçõeskid
para os valores de chave pública.https://www.gstatic.com/iap/verify/public_key-jwk
: este URL contém as chaves públicas do IAP no formato JWK (em inglês).
Depois de conseguir a chave pública, use uma biblioteca de JWT para verificar a assinatura.
Como verificar o payload do JWT
Verifique se o payload do JWT está em conformidade com as seguintes restrições:
Declarações de payload do JWT | ||
---|---|---|
exp |
Tempo de expiração | Precisa estar no futuro. O tempo é medido em segundos desde a era UNIX. Defina 30 segundos para a defasagem. A vida útil máxima de um token é de 10 minutos + 2 * distorção. |
iat |
Hora de emissão | Precisa estar no passado. O tempo é medido em segundos desde a era UNIX. Defina 30 segundos para a defasagem. |
aud |
Público-alvo | Precisa ser uma string com os seguintes valores:
|
iss |
Emissor | Precisa ser https://cloud.google.com/iap . |
hd |
Domínio da conta | Se uma conta pertencer a um domínio hospedado, a declaração hd será fornecida para diferenciar o domínio a que a conta está associada. |
google |
Declaração do Google |
Se um ou mais níveis de acesso
se aplicam à solicitação, os nomes são armazenados no google
objeto JSON da declaração, sob a chave access_levels , como uma matriz
de strings.
Quando você especifica uma política do dispositivo e a organização tem acesso aos dados do dispositivo, o |
É possível conseguir os valores para a string aud
mencionada acima acessando o
console do Google Cloud ou a ferramenta de linha de comando gcloud.
Para extrair os valores de string aud
no console do Google Cloud, acesse a
Configurações do Identity-Aware Proxy
do seu projeto, clique em Mais ao lado do recurso "Balanceador de carga" e, em seguida,
selecione Público-alvo do JWT com cabeçalho assinado. A caixa de diálogo JWT com cabeçalho assinado exibida mostra a declaração aud
do recurso selecionado.
Se você quiser usar a CLI gcloud
ferramenta de linha de comando gcloud para extrair os valores de string aud
, você precisa saber
o ID do projeto. O ID do projeto está na página
Console do Google Cloud
Informações do projeto e execute os comandos especificados abaixo para cada valor.
Número do projeto
Para conseguir o número do projeto usando a ferramenta de linha de comando gcloud, execute o seguinte comando:
gcloud projects describe PROJECT_ID
O comando retorna uma saída semelhante a esta:
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 conseguir o código do serviço usando a ferramenta de linha de comando gcloud, execute o seguinte comando:
gcloud compute backend-services describe SERVICE_NAME --project=PROJECT_ID --global
O comando retorna uma saída semelhante a esta:
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
Como recuperar a identidade do usuário
Se todas as verificações acima forem bem-sucedidas, recupere a identidade do usuário. O payload do token de código contém as seguintes informações de usuário:
Identidade do usuário de payload do token de código | ||
---|---|---|
sub |
Assunto |
O identificador exclusivo e estável do usuário. Use esse valor em vez do cabeçalho x-goog-authenticated-user-id .
|
email |
E-mail do usuário | O endereço de e-mail do usuário.
|
Veja alguns códigos de amostra para proteger um app com cabeçalhos assinados pelo IAP:
C#
Go
Java
Node.js
PHP
Python
Ruby
Como testar seu código de validação
Se você visitar seu app usando os
parâmetros de consulta secure_token_test
,
o IAP incluirá um JWT inválido. Use-o para garantir que a lógica de validação do JWT processe todos os diversos casos de falha e para ver como seu app se comporta ao receber um JWT inválido.
Como criar uma exceção de verificação de integridade
Como mencionado anteriormente, as verificações de integridade do Compute Engine e do GKE não usam cabeçalhos JWT. Além disso, o IAP não processa verificações de integridade. Você precisará configurar a verificação de integridade, bem como o app para permitir o acesso nessa situação.
Como configurar a verificação de integridade
Se você ainda não definiu um caminho para a verificação de integridade, use o Console do Google Cloud para definir um caminho não confidencial para a verificação de integridade. Esse caminho não pode ser compartilhado com nenhum outro recurso.
- Acessar o console do Google Cloud
Verificações de integridade.
Acessar a página "Verificações de integridade" - Clique na verificação de integridade que você está usando no app e depois clique em Editar.
- Em Caminho da solicitação, insira um nome de caminho não confidencial. Isso especifica o caminho do URL que o Google Cloud usará para enviar solicitações de verificação de integridade.
Se omitido, a solicitação de verificação de integridade será enviada para
/
. - Clique em Salvar.
Como configurar a validação do JWT
No código que chama a rotina de validação do JWT, inclua uma condição para exibir um status HTTP 200 ao caminho de solicitação da verificação de integridade. Por exemplo:
if HttpRequest.path_info = '/HEALTH_CHECK_REQUEST_PATH' return HttpResponse(status=200) else VALIDATION_FUNCTION
JWTs para identidades externas
Se você estiver usando o IAP com identidades externas, ele ainda emitirá um JWT assinado em todas as solicitações autenticadas, assim como faz com as identidades do Google. No entanto, existem algumas diferenças.
Informações do provedor
Ao usar identidades externas, o payload do JWT incluirá uma declaração chamada gcip
. Essa declaração contém informações sobre o usuário, como o e-mail e o URL da foto, além de outros atributos específicos do provedor.
Veja abaixo um exemplo de um JWT de um usuário que fez login 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"
}',
Campos email
e sub
Se um usuário foi autenticado pelo Identity Platform, os campos email
e sub
do JWT terão como prefixo o emissor do token do Identity Platform e o ID do locatário usado (se houver). Por exemplo:
"email": "securetoken.google.com/PROJECT-ID/TENANT-ID:demo_user@gmail.com", "sub": "securetoken.google.com/PROJECT-ID/TENANT-ID:gZG0yELPypZElTmAT9I55prjHg63"
Como controlar o acesso com sign_in_attributes
O IAM não é compatível com identidades externas. No entanto, é possível usar as declarações incorporadas no campo sign_in_attributes
para controlar o acesso. Por exemplo, imagine que um usuário fez login usando um provedor 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"
}
É possível adicionar lógica ao aplicativo de maneira semelhante ao código abaixo para restringir o acesso a usuários com um papel válido:
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.
}
Para acessar os demais atributos de usuário dos provedores SAML e OIDC do Identity Platform, use a declaração aninhada gcipClaims.gcip.firebase.sign_in_attributes
.
Limitações de tamanho das declarações do IdP
Depois que um usuário faz login com o Identity Platform, os atributos adicionais do usuário são propagados para o payload do token de ID do Identity Platform sem estado, que é transmitido com segurança para o IAP. O IAP vai em seguida, emite o próprio cookie opaco sem estado que também contém as mesmas declarações. O IAP gera o cabeçalho JWT assinado com base no cookie conteúdo.
Por isso, se uma sessão for iniciada com um grande número de reivindicações, ela poderá excedem o tamanho máximo de cookie permitido, que é de aproximadamente 4 KB na maioria dos navegadores. Isso causará uma falha no login.
Garantir que apenas as declarações necessárias sejam propagadas no IdP SAML ou atributos OIDC. Outra opção é usar funções de bloqueio para filtrar 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,
};
}
});