Aumentando a segurança das suas aplicações no Azure com Managed Identities
Eliminando credenciais do seu código.
A evolução dos mecanismos de segurança para gerenciar credenciais no desenvolvimento de software percorreu um longo caminho. Começamos com encriptação em arquivos de configuração, passamos pela automação de deploys em pipelines de CI/CD e pelo uso de User Secrets. Depois, os cofres de chaves como o Azure Key Vault ajudaram a centralizar e proteger as credenciais. Uma abordagem interessante é o uso de Azure Managed Identities, que elimina a necessidade de gerenciar diretamente as credenciais, permitindo a autenticação e autorização entre serviços de forma segura. A escolha do melhor mecanismo depende das necessidades e requisitos específicos do projeto
Considere o cenário abaixo
No cenário acima, é necessário pelo menos uma credencial no código para acessar o banco de dados, conhecida como “connection string”. Além disso, dependendo das ações a serem realizadas com o APIM (Azure API Management), também é necessário uma credencial para negociar tokens com o AAD (Azure Active Directory) e repassá-los para a API. Se você quiser saber mais sobre esse cenário, recomendo dar uma olhada neste artigo. Azure AD B2C / Azure AD, usando Client Credencial
Modelo Convencional de registro de Aplicações no Azure Active Directory
Normalmente, é necessário ter uma aplicação que representa o servidor e outra que representa o cliente. Em seguida, expomos uma URI na aplicação do servidor e concedemos permissões nessa URI para a aplicação cliente.
No caso de fluxo de usuário, usamos escopos para definir as permissões. Já no caso de fluxo de aplicação, utilizamos roles (funções) para definir as permissões.
Com as identidades gerenciadas, o processo é semelhante, mas as aplicações serão criadas automaticamente quando habilitarmos o serviço de Identidade Gerenciada no recurso correspondente. Portanto, o primeiro passo para utilizar as Managed Identities no cenário mencionado é habilitar esse recurso no Azure para a instância do App Service e, em seguida, realizar algumas configurações no Azure SQL Server.
No App Services
System assigned
Uma identidade gerenciada atribuída ao sistema é restrita a uma por recurso e está vinculada ao ciclo de vida desse recurso. Você pode conceder permissões à identidade gerenciada usando o controle de acesso baseado em função do Azure (Azure RBAC). A identidade gerenciada é autenticada com o Azure AD, portanto, você não precisa armazenar nenhuma credencial no código.
User assigned
As identidades gerenciadas atribuídas pelo usuário permitem que os recursos do Azure se autentiquem em serviços de nuvem (por exemplo, Azure Key Vault) sem armazenar credenciais no código. Esse tipo de identidades gerenciadas são criadas como recursos autônomos do Azure e têm seu próprio ciclo de vida. Um único recurso (por exemplo, máquina virtual) pode utilizar identidades gerenciadas atribuídas a vários usuários. Da mesma forma, uma única identidade gerenciada atribuída ao usuário pode ser compartilhada entre vários recursos (por exemplo, máquina virtual).
Depois disso podemos começar com nossa Connectionstring direto no código, ela vai mudar disso
Data Source=seed-srv.database.windows.net;Initial Catalog=seeddb;user id=seed-app;password=123456;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;MultipleActiveResultSets=True
Para isso
Server=seed-srv.database.windows.net; Authentication=Active Directory Managed Identity; Database=seeddb
Perceba que não há mais credenciais na connection string e não foi necessário fazer nenhuma alteração no código. Essa implementação funciona bem no dotnet core 3.1 e 5.0. No entanto, é importante notar que você precisa atualizar ou instalar o pacote Microsoft.Data.SqlClient para a versão 3.0.0. Recomendo dar uma olhada aqui para obter mais informações sobre essa questão, pois pode variar dependendo da versão do framework da sua aplicação.
Na versão Full framework 4.7.2 ≥ não foi bem assim
Foi necessário obter o token manualmente e passá-lo para a conexão. O código para Entity Framework (E.F.) é mais ou menos o mesmo, porém, no caso do E.F., precisamos fazer isso na classe de contexto, no construtor.
Install-Package Microsoft.Azure.Services.AppAuthentication
A connectionstring também ficou diferente.
Server=seed-srv.database.windows.net; Database=seeddb
OBS: Aqui fica um ponto de estudo para verificar se realmente é necessário intervir no código para usar a identidade gerenciada.
Do lado do Azure Sql Server também tem alguns ajustes
Primeiramente, no seu servidor do Azure SQL Server, vá até as configurações de segurança e selecione a opção “Firewalls and Virtual Networks”. Em seguida, marque a opção “Allow Azure services and resources to access this server”. Isso permitirá que qualquer recurso dentro da infraestrutura do Azure acesse o banco de dados, eliminando a necessidade de liberar manualmente endereços IP.
Outro ponto importante é que precisaremos criar um usuário no Azure SQL Server para o banco de dados desejado. No entanto, para fazer isso, é necessário fazer login com um usuário do Azure Active Directory (AD). Portanto, é necessário configurar um usuário administrador do Azure SQL Server que esteja vinculado ao Azure Active Directory. Para fazer isso, vá até o menu “Settings” e, em seguida, selecione o submenu “Azure Active Directory” no servidor do Azure SQL Server. Você pode criar esse usuário no Azure Active Directory e associá-lo aqui sem enfrentar maiores problemas.
Pelo CLI
az sql server ad-admin create --resource-group <ResourceGroupName> --server-name <ServerName> --display-name ADMIN --object-id <ObjectId>
Agora podemos logar no SSME utilizando a opção MFA,
Depois de conectar rode o script a seguir contra o banco desejado.
CREATE USER [seed-api-windows] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [seed-api-windows];
ALTER ROLE db_datawriter ADD MEMBER [seed-api-windows];
ALTER ROLE db_ddladmin ADD MEMBER [seed-api-windows];
GO
Caso precise dar grant em alguma procedure use algo como.
GRANT EXECUTE TO [seed-api-windows]
Esse usuário precisa ter o nome da Identidade gerenciada criada na aplicação acima, normalmente é o mesmo nome do recurso.
Mas e o APIM?
Vamos considerar como requisito que nossa API, descrita no diagrama acima, esteja protegida pelo Azure Active Directory (AAD) e, portanto, requer um token para acesso. No entanto, os usuários que consomem o Azure API Management (APIM) fornecerão apenas uma chave de assinatura (subscription key). Nesse caso, é responsabilidade do APIM negociar um token com o AAD e repassá-lo para a API.
No artigo “Azure AD B2C / Azure AD, usando Client Credential”, esse cenário é detalhado de forma mais abrangente. Nele, podemos ver que a política “send-request” utilizada requer as credenciais de aplicação para obter o token usando o fluxo de credenciais do cliente. Nosso objetivo é substituir essa política por uma solução mais adequada.
authentication-managed-identity
O primeiro passo é habilitar a Identidade Gerenciada no Azure API Management (APIM). Para fazer isso, siga estas etapas:
- Acesse sua instância do APIM no portal do Azure.
- Vá para o menu “Security” (Segurança).
- Selecione o subitem “Managed Identities” (Identidades Gerenciadas).
- Ative a opção para ligar a identidade gerenciada, definindo a flag como “On”.
Ao habilitar a identidade gerenciada no APIM, você permite que ele tenha uma identidade exclusiva e gerenciada pelo Azure. Isso permite que o APIM faça autenticação e autorização em outros serviços do Azure de forma segura e simplificada
No modelo padrão de registro de aplicações, temos uma aplicação servidor e uma aplicação cliente. A aplicação servidor expõe uma URI e, por meio de roles ou escopos, solicitamos um token de autenticação. O Azure API Management (APIM), nesse caso, desempenha o papel de cliente, solicitando um token à aplicação servidor.
A sequência típica de interações seria a seguinte:
- A aplicação cliente (APIM) envia uma solicitação de token para a aplicação servidor, especificando as roles ou escopos necessários.
- A aplicação servidor, por meio de integração com o Azure AD, autentica a solicitação e emite um token válido com as permissões adequadas.
- A aplicação cliente (APIM) recebe o token de autenticação da aplicação servidor.
- O APIM pode então usar esse token para autenticar e autorizar as solicitações de API vindas de seus consumidores (clientes).
Esse modelo permite que o APIM, como intermediário entre a aplicação cliente e a aplicação servidor, solicite um token de autenticação para obter acesso seguro aos recursos da aplicação servidor.
OBS: O que fiz aqui foi registrar mais uma aplicação para representar a API, então expor uma URI e cadastrar uma Role de permissionamento. Atenção essa aplicação é diferente da gerada como Enterprise Application no processo de identidade gerenciada pois preciso utilizar os recursos de Exposição de URI e Roles que não existem nas aplicações Enterprise.
Aqui fica outro ponto de estudo para verificar se é possível utilizar a mesma aplicação gerada no processo de ativação da identidade do wep app.
Com essa aplicação devidamente configurada, a API foi protegida usando o seguinte código adicionado ao método ConfigureServices da classe Startup.cs em uma aplicação web dotnet core 3.1 (e também válido para versões superiores).
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");
No appsettings.json temos algo assim
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "7a3837da-8577-4677-9fcd-e66362b054c1",
"TenantId": "779811d8-5862-4c34-baeb-6b53957d52e7",
"Audience": "7a3837da-8577-4677-9fcd-e66362b054c1"
},
A audiência é representada pelo ClientId, que é usado para restringir o acesso apenas a essa aplicação específica. No entanto, no caso do Azure API Management (APIM), a aplicação gerada durante o processo de ativação da Identidade Gerenciada é uma “Enterprise Application” que não possui uma interface de usuário para configurar permissões. Portanto, para lidar com essa situação, precisaremos fazer isso por meio do módulo PowerShell do AzureAD.