msdn21_capa.JPG

Clique aqui para ler todos os artigos desta edição


App Lockdown Defenda seus Aplicativos e Informações Críticas de Usuário com Técnicas de Codificação de Defesa

 por: Kenny Kerr  

 

No mundo conectado dos dias atuais, em que todo aplicativo é um alvo em potencial, você

precisa ampliar seus esforços de programação de defesa para dar conta da segurança.

Tudo o que você aprendeu sobre a programação de defesa o ajudará a escrever um código

mais seguro, mas isso não é suficiente. Você precisa ir muito além para criar defesas explícitas em seu software.

 

Neste artigo, tratarei da proteção de usuários, da proteção de credencias e de informações confidenciais e da defesa de servidores. Abordarei uma ampla gama de cenários de programação comuns e explorarei maneiras práticas por meio das quais você poderá escrever um código mais resistente

a ataques.

 

Protegendo credenciais

Vamos começar pela maneira como você pode proteger as credenciais de seus usuários. Para melhor segurança de credenciais, você deve evitar gerenciar explicitamente as credenciais do usuário porque as senhas devem ser mantidas em segredo, e segredos são difíceis de proteger. Desse modo, você deve depender de sessões de logon e de signon simples. Caso precise executar um aplicativo como um usuário diferente, use a opção Run As do shell ou digite run as no prompt de comando. No entanto, se estiver em apuros e precisar gerenciar por conta própria as credenciais, veja o que você deverá fazer.

 

Em primeiro lugar, você precisará instalar o Platform SDK, release de fevereiro 2003 ou posterior, para usar as funções descritas aqui. Uma vez instalado o Platform SDK, adicione os diretórios Include e Lib aos caminhos de busca (search path) do Visual C++. Se você preferir usar o P/ Invoke a partir do C# ou do Visual Basic® .NET, não será necessário instalar o Platform SDK.

 

A próxima etapa será solicitar as credenciais do usuário. Para possibilitar uma experiência de usuário consistente e gravar menos código relacionado à segurança, use a função CredUIPromptForCredentials para aplicativos de interface de usuário e CredUICmdLinePromptForCredentials para aplicativos de console. Basicamente, ambas oferecem os mesmos recursos, sendo que a versão de GUI fornece algumas opções extras específicas à interface gráfica, tais como permitir que você altere os bitmaps do banner padrão, a legenda e a mensagem da caixa de diálogo. Essas funções podem ser usadas para solicitar credenciais de domínio do Windows ou genéricas, conforme ilustrado na Listagem 1.

 

As credenciais são armazenadas no perfil do usuário e usam o nome de destino como chave, por isso certifiquese de usar nomes de destino exclusivos e sugestivos. A estrutura CREDUI_INFO permite que você especifique um handle para a janela da caixa de diálogo restrita. Além disso, permite que você substitua a legenda padrão, o texto da mensagem e o bitmap do banner que será exibido ao usuário.

 

Em seguida, existe a questão do estouro de inteiros  (integer overflow). Você reparou nas duas operações static_cast? Elas são usadas para converter o resultado do método std::vector::size em ULONG, que é o esperado pela função CredUIPromptForCredentials. Embora ULONG seja sempre um inteiro não assinado de 32 bits, size_t é suficientemente grande para alcançar a faixa completa de um ponteiro (span the full range of a pointer).

 

Em outras palavras, size_t é um inteiro não assinado de 32 bits em um Windows de 32 bits, e um inteiro não assinado de 64 bits em um Windows de 64 bits. A operação static_ cast é usada para suprimir o aviso do compilador que informa o usuário de que o cast pode não ser seguro em

versões de Windows de 64 bits. Ao suprimir o aviso do compilador, você será totalmente

responsável por garantir que o cast seja sempre seguro, mesmo quando executado em plataformas de 64 bits. No exemplo anterior, você pode ver que o tamanho do vetor é menor que std::numeric_limits::max(), o que significa que, neste caso, o cast é seguro. Nem é preciso

mencionar que existe muito espaço para erros e, por esse motivo, escrevi a classe PromptForCredentials para facilitar e o gerenciamento de credenciais e dar mais segurança

ao processo. A classe está disponível no código para download deste artigo. O exemplo a seguir mostra a classe:

 

PromptForCredentials em uso:

PromptForCredentials prompt;

prompt.Target(L”server”);

prompt.ParentWindow(0); // definir para a janela pai

prompt.Flags(CREDUI_FLAGS_GENERIC_CREDENTIALS);

if (prompt.ShowDialog()) {

// TODO: use o prompt.UserName() e o prompt.Password()

prompt.ScrubPassword();

}

 

Como você pode ver, o código é simples e se encarrega do tratamento de erros. O destrutor PromptForCredentials chamará automaticamente o método ScrubPassword, mas é recomendável retirar todos os dados confidenciais da memória assim que possível. Internamente, o método ScrubMemory chama SecureZeroMemory (que é apenas um substituto da função RtlSecureZeroMemory) para

zerar o buffer da senha.

 

Para permitir que você teste mais facilmente a ampla gama de opções e sinalizadores fornecidos pela função CredUIPromptForCredentials, escrevi o aplicativo de exemplo Prompt for Credentials exibido na Figura 1. Ele permite que você combine as diferentes opções e sinalizadores

até encontrar o recurso de que necessita.

 

Protegendo dados no cliente

A proteção de dados confidenciais envolve criptografar os dados enquanto eles estão na memória e antes de eles serem gravados em disco. Se os dados forem persistidos, eles também poderão ser protegidos por uma lista de controle de acesso rígida (ACL - Access control list).

 

Esta seção descreverá como executar corretamente essas tarefas. Com boas bibliotecas de criptografia de dados (Crypto API, CAPICOM e System.Security.Cryptography) totalmente disponíveis, escrever um código de criptografia é muito fácil, mas o gerenciamento de chaves e senhas de criptografia continua sendo um desafio. O Windows XP e o Windows Server® 2003 possuem funções de proteção de dados que permitem que você criptografe e descriptografe os dados sem precisar gerenciar as chaves de criptografia.

 

É possível usar a função CryptProtectData para criptografar dados que só poderão ser descriptografados por meio da função CryptUnprotectData quando chamada a partir de uma sessão de logon baseada na mesma conta de usuário. A Listagem 2 mostra um exemplo.

  

 

Listagem 1. Usando a função CredUIPromptForCredentials

 

std::wstring target = L”server”;

std::vector userName(CREDUI_MAX_USERNAME_LENGTH + 1);

std::vector password(CREDUI_MAX_PASSWORD_LENGTH + 1);

CREDUI_INFO info = { sizeof (CREDUI_INFO) };

info.hwndParent = 0; // definir para a janela pai

DWORD result = ::CredUIPromptForCredentials(&info,

target.c_str(), 0 /*reservado*/, 0 /*erro auth não usado*/,

&userName[0], static_cast(userName.size()),&password[0],

static_cast(password.size()), 0 /*ignore save state*/,

CREDUI_FLAGS_GENERIC_CREDENTIALS);

if (NO_ERROR == result) {

// Usuário escolhe OK.

} else if (ERROR_CANCELLED == result) {

// Usuário escolhe Cancel (Cancelar).

} else {

// Ocorreu um erro.

}

 

image002.gif

Figura 1. Exemplo do aplicativo Prompt for Credencials

 

Listagem 2. Usando CriptProtectData paraCriptografar Dados

 

std::vector buffer;

// TODO: transfira os dados confidenciais do usuário para o

// buffer

DATA_BLOB plaintext = { static_cast(buffer.size()),

&buffer[0] };

DATA_BLOB ciphertext = { 0 };

CheckError(::CryptProtectData(&plaintext, 0 /*no description*/,

...

Quer ler esse conteúdo completo? Tenha acesso completo