Com o objetivo de diminuir os riscos, o AWS Secrets Manager nos permite substituir variáveis de ambiente codificadas por um serviço, tornando transparente o processo de recuperação de dados diferenciados por ambiente.

Neste artigo, você conhecerá uma solução inteligente de acesso a parâmetros da aplicação utilizando o AWS Secrets Manager.

Como parametrizar as aplicações

É comum nos depararmos com situações em que dados sensíveis ficam expostos em arquivos de configurações ou até mesmo “cravados” no próprio código-fonte da aplicação.

Felizmente, o uso desse tipo de estratégia de parametrização está diminuindo e uma das alternativas é o uso das variáveis de ambiente.

Essa estratégia consiste na utilização das variáveis de ambiente para persistir configurações como endereços de conexão com banco de dados, URLs de acesso etc.

No entanto, a segurança dos dados pode estar comprometida caso você não tome os devidos cuidados, por exemplo, realizar a criptografia antes de armazenar os dados sensíveis, isto é, quando falamos em acessar ambientes diferentes (desenvolvimento, homologação, produção etc.), automaticamente falamos em valores diferentes para cada ambiente que devem estar referenciados em classes e métodos no código-fonte.

Modificar o código-fonte de um sistema pode ser relativamente complicado, além de aumentar o risco de efeitos colaterais indesejáveis, principalmente quando são realizadas alterações em classes ou métodos que são utilizados por diversas partes do sistema.  

Outra questão muito importante para se levar em consideração é a publicação de uma nova versão que é submetida aos processos de compilação, testes unitários, testes retroativos, aprovação para implantação etc., podendo variar de acordo com o projeto, mas que, inevitavelmente, sempre consome tempo da equipe de desenvolvimento.

Entretanto, uma outra estratégia para uso de variáveis de ambiente é através do AWS Secrets Manager.

O que são variáveis ​​de ambiente?

Uma variável de ambiente é um valor definido fora da aplicação, sempre relacionado a uma chave de busca, incorporado ao sistema operacional ou ao contexto de um microsserviço.

O principal motivo para utilizarmos variáveis ​​de ambiente é limitar a necessidade de modificar e relançar um aplicativo devido a alterações nos dados de configuração.

Por exemplo, no código abaixo a URL de autenticação está fixada no código-fonte da aplicação.

static void Autenticar()
{
  Access access = “http://test.salesforce.com/”.PostUrlEncodedAsync(new
       {
             grant_type = "password",
             client_id = "uirf9f8uifsujfssfhfhskjkjldjkfjujkfs45f54f454sf4sf",
             client_secret = "897897845855",
             password = "123456",
             username = "fakeuser@fake.com.br"
       }).ReceiveJson<Access>().Result;
}

Exemplo 1 – A URL é recuperada de uma variável de ambiente

No entanto, no próximo código a mesma URL é recuperada de uma variável de ambiente.

static void Autenticar()
{
  Access access = Environment.GetEnvironmentVariable("URL_AUTENTICACAO")
         .PostUrlEncodedAsync( new
         {
             grant_type = "password",
             client_id = "uirf9f8uifsujfssfhfhskjkjldjkfjujkfs45f54f454sf4sf",
             client_secret = "897897845855",
             password = "123456",
             username = "fakeuser@fake.com.br"
         }).ReceiveJson<Access>().Result;
}

Exemplo 2 – URL fixada dentro do código-fonte

AWS Secrets Manager

O recurso criado pela AWS para garantir a segurança dos dados utilizados por aplicações ou serviços, simplifica todo o trabalho de criptografia e gestão de dados, ou seja, o AWS Secrets Manager armazena os dados sensíveis para uma aplicação ou serviço, sejam senhas, chaves de acesso ou objetos serializados em formato JSON.

Além disso, é possível configurar a substituição dos segredos das chaves de maneira automática, reduzindo significativamente o risco de comprometimento de suas senhas.

Um exemplo seria a alteração da chave privada responsável pela geração de um token de autenticação ou até mesmo a alteração da senha de um banco de dados.

Análise de caso

Com o intuito de demonstrarmos a usabilidade do AWS Secrets Manager, usaremos como exemplo uma aplicação .Net hospedada como serviço no AWS Lambda que necessita de alguns parâmetros para operar, sendo que eles podem ou não estar armazenados em variáveis de ambiente.

ChaveValor
CONNECTION_STRINGServer=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;
SERVICO_CONSULTA_CLIENTEShttp://servidor/clientes/obter
CHAVE_ACESSO_CONSULTA_CLIENTES4721d25f714c477a981ed08b38ca01ae
SERVIDOR_EMAILmail.seudominio.com.br 
MENSAGEM_BANCO_FORA_ARO banco de dados não está disponível no momento
Tabela 1 – Exemplos de parâmetros de aplicação

Como podemos ver, pelos menos dois dados sensíveis estão expostos, já que se referem às informações de acesso ao banco de dados e à chave de consulta de um serviço externo.

Desta forma, a melhor solução seria armazenar esses valores no AWS Secrets Manager, já que ele garantiria a criptografia da informação, mas lembre-se que é necessário realizar alterações no código.

Em seguida, vamos considerar um cenário em que a aplicação acessa dois bancos de dados diferentes e dois serviços REST utilizando uma chave (API KEY), além de um parâmetro para informar um caminho físico no disco.

Para que as configurações sejam encontradas corretamente, será necessário incluir uma anotação nas propriedades que deseja popular com os valores obtidos da variável de ambiente especificada no atributo EnvironmentVariableAttribute.

public class MyAppSettings : IMyAppSettings
    {
        [EnvironmentVariableAttribute("ASPNETCORE_ENVIRONMENT", true)]
        public AppEnvironment Environment { get; set; }

        public Databases Databases { get; set; }

        public ExternalServices ExternalServices { get; set; }

        [EnvironmentVariableAttribute("PATH_SAVE_FILE", true)]
        public string PathSaveFile { get; set; }
    }
public class Databases: IDatabases
    {
        [EnvironmentVariableAttribute("Connection_String_Oracle", true)]
        public string ConnectionStringOracle { get; set; }

        [EnvironmentVariableAttribute("Connection_String_SQLServer", true)]
        public string ConnectionStringSQLServer { get; set; }
    }

Exemplo 3 – Uso do atributo EnvironmentVariableAttribute

Observe que é possível incluir propriedades de tipos complexos referenciando outras classes que contenham parâmetros necessários para sua aplicação. Isso permite uma melhor organização do código e distribuição de contextos de parâmetros.

Na injeção de dependência da aplicação, deve-se utilizar o método “AddSettings” informando o nome da sua classe principal de parâmetros. No exemplo utilizamos a interface/classe IMyAppSettings/MyAppSettings, para que sejam “populadas” todas as propriedades que estiverem com a notação EnvironmentVariableAttribute.

public static void ConfigureServices()
{
      serviceCollection = new ServiceCollection();
      serviceCollection.AddSettings<IMyAppSettings, MyAppSettings>();
      serviceCollection.AddTransient<ISampleService, SampleService>();
      serviceProvider = serviceCollection.BuildServiceProvider();
      serviceScope = serviceProvider.CreateScope();
}

Exemplo 4 – Configuração do serviço

A configuração das variáveis deve ser realizada diretamente no arquivo launchSettings.json dentro da pasta properties, conforme exemplo abaixo:

"environmentVariables": {
        "SECRETMANAGER_PREFIXDELIMITER": ":",
        "SECRETMANAGER_PREFIX": "SECRET_KEY",
        "SECRETMANAGER_REGION": "us-east-1",
        "SECRETMANAGER_SECRETNAME": "MySamplesSecrets",
        "SECRETMANAGER_VERSIONSTAGEDEFAULT": "AWSCURRENT",

        "ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
        "URL_SERVICE_SALES": "http://sales/service",
        "API_Key_Service_Sales": "1212121212",
        "URL_SERVICE_PRODUCTS": "http://products/service",
        "API_Key_Service_Products": "7878787878",
        "CONNECTION_STRING_ORACLE": "String de conexão com o Oracle",
        "CONNECTION_STRING_SQLSERVER": "String de conexão com o SQL Server",
        "PATH_SAVE_FILE": "\"D:\\temp\\\""
}

Exemplo 5 – Configuração de variáveis no launchSettings.json

Assim, ao executar a aplicação os valores contidos nas variáveis de ambiente serão obtidos:

Imagem 1 – Resultado da execução

Migrando as configurações para o AWS Secrets Manager

Imagine que um analista da área de segurança detecte alguns parâmetros da aplicação que não podem ficar expostos em variáveis de ambiente, sendo necessário migrá-los imediatamente para o serviço do AWS Secrets Manager, caso contrário o software poderá ser pontuado como uma “aplicação de risco” o que trará consequência para a área responsável em mantê-lo.

Antes de mais nada, para promover essa migração, é necessário configurar os parâmetros responsáveis por direcionar a aplicação e permitir que os dados sejam recuperados do serviço do AWS Secrets Manager.

ParâmetroDescrição
“SECRETMANAGER_PREFIXDELIMITER”: “:”,Indica qual caractere será utilizado para separar o prefixo do nome da chave.
“SECRETMANAGER_PREFIX”: “SECRET_KEY”,Prefixo que será informado na variável de ambiente, seguido do nome da chave.
“SECRETMANAGER_REGION”: “us-east-1”,Região em que está hospedado o serviço da AWS.
“SECRETMANAGER_SECRETNAME”: “MySamplesSecrets”,Nome provisionado para o serviço da AWS.
“SECRETMANAGER_VERSIONSTAGEDEFAULT”: “AWSCURRENT”,Versão que você deseja utilizar[1]. Por padrão utilizamos sempre o último.
Tabela 2 – Configurações para o AWS Secrets Manager

Desse modo, os parâmetros para a aplicação serão gerenciados pela AWS:

Imagem 2 – Interface para inclusão dos dados sensíveis

E as novas configurações das variáveis de ambiente da aplicação passam a referenciar as chaves configuradas na AWS:

"environmentVariables": {
        "SECRETMANAGER_PREFIXDELIMITER": ":",
        "SECRETMANAGER_PREFIX": "SECRET_KEY",
        "SECRETMANAGER_REGION": "us-east-1",
        "SECRETMANAGER_SECRETNAME": "UK",
        "SECRETMANAGER_VERSIONSTAGEDEFAULT": "AWSCURRENT",
        "ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
        "URL_SERVICE_SALES": "http://sales/service",
        "URL_SERVICE_PRODUCTS": "http://products/service",
        "PATH_SAVE_FILE": "\"D:\\temp\\\""
        "API_Key_Service_Sales": "SECRET_KEY:API_Key_Service_Sales ",
        "API_Key_Service_Products": "SECRET_KEY:API_Key_Service_Products",
        "CONNECTION_STRING_ORACLE": "SECRET_KEY:CONN_ORACLE",
        "CONNECTION_STRING_SQLSERVER": "SECRET_KEY:CONN_SQLSERVER",
}

Exemplo 6 – Configuração das variáveis na aplicação

Concluindo…

Configurar sistemas através do uso de parâmetros que alteram o comportamento da aplicação é uma prática adotada há muitos anos e que, provavelmente, continuarão sendo fortes aliados dos programadores mundo a fora.

Uma aplicação bem arquitetada permite a seus mantenedores entender com mais clareza o comportamento de suas funcionalidades e, se necessário, alterar estes parâmetros sem a necessidade de recompilar e realizar uma nova implantação.

Lembre-se: o ciclo de vida de um software vai muito além do seu desenvolvimento e o custo final está fortemente relacionado com o custo de manutenção, quanto menos tempo se investir em processos de ajustes e correções menor será esse custo.

Procure identificar os pontos onde, futuramente, poderão ocorrer alterações e externalize os valores em forma parâmetros. Desta forma você mitigará possíveis “dores de cabeça”!

Clique aqui e acesse o código fonte.

*Para testar a aplicação completa você precisará do SDK .Net Core 3.1, bem como o Visual Studio 2019.