C# - Usando a classe SecureString


Vamos rever o conceito de SecureString na linguagem C#.

Se você utiliza strings para armazenar informações confidenciais ou críticas como senha de usuário, número do cartão de crédito, etc., saiba que os objetos String contém um array de caracteres em memória que podem ser acessados por código inseguro ou não gerenciado.

Mesmo que o objeto String for usado por apenas um curto período de tempo e em seguida descartado pela coleta de lixo (garbage coletor), a CLR não pode reutilizar imediatamente a memória do objeto string e desta forma os caracteres strings permanecem na memória onde podem ser acessados.

Outro detalhe a considerar é que strings são imutáveis e quando você as manipula as cópias antigas permanecem na memória e você acaba ficando com versões diferentes da string espalhadas por toda a memória.(As instâncias são somente leitura e não é possível prever quando a instância será excluída da memória)

Diante deste cenário conclui-se que é arriscado armazenar informações confidenciais, como uma senha, número de cartão de crédito ou dados pessoais em um objeto string. Existe sempre o risco da informação ser revelada depois de sua utilização pois sua aplicação não pode apagar os dados da memória.

Mas como fazer para poder armazenar informações na sua aplicação que envolvam dados confidenciais e críticos de forma segura ?

A plataforma .NET possui no namespace System.Security a classe SecureString que pode ser usada para este objetivo, sendo muito similar a classe String e também armazena valores alfanuméricos.

A diferença é que os valores armazenados em objetos SecureString são automaticamente criptografados, podem ser modificados até que seu aplicativo marque-os como somente leitura, e podem ser apagados da memória do computador por seu aplicativo ou pelo coletor de lixo da plataforma .NET.

O valor de uma instância da classe SecureString é criptografado automaticamente quando uma instância é inicializada ou quando o valor é modificado, e sua aplicação pode processar a instância imutável e evitar uma nova alteração, invocando o método MakeReadOnly.

Assim a classe SecureString representa um texto que deve ser mantido em sigilo. O texto é criptografado para privacidade quando está sendo usado e excluído da memória do computador quando não for mais necessário. (Esta classe não pode ser herdada.)

Quando você constrói um objeto SecureString, atribui internamente um bloco de memória não gerenciado que contém uma matriz de caracteres.

Esses caracteres strings são criptografados de forma a proteger as informações sensíveis a partir de qualquer código inseguro ou malicioso. Você pode acrescentar, inserir, remover ou definir um caractere na string segura usando qualquer um dos seguintes métodos: AppendChar, InsertAt, RemoveAt e SetAt.

Sempre que você chamar qualquer um destes métodos, internamente, os métodos decifram os caracteres, executam a operação e a seguir cifram novamente os caracteres. Dessa forma os caracteres ficam em um estão não criptografado por um espaço de tempo muito curto. Naturalmente isso causa um impacto no desempenho das operações e isso você tem que levar em conta ao usar este recurso. Use com cautela e somente quando necessário.

Este recurso esta disponível a partir da versão 2.0 da plataforma .NET.

Para criar uma SecureString temos que definir o namespace System.Security, criar uma instância da classe SecureString e acrescentar cada caractere usando o método AppendChar:

 SecureString secstr = new SecureString();
            secstr.AppendChar('M');
            secstr.AppendChar('a');
            secstr.AppendChar('c');
            secstr.AppendChar('o');
            secstr.AppendChar('r');
            secstr.AppendChar('a');
            secstr.AppendChar('t');
            secstr.AppendChar('t');
            secstr.AppendChar('i');

Quando a cadeia tiver os dados que você desejar, você pode torná-lo imutável e somente-leitura chamando o método MakeReadOnly:

secstr.MakeReadOnly();

Para ler o conteúdo da SecureString use o método SecureStringToBSTR():

IntPtr ponteiro = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(secstr);
string StringDecifrada = System.Runtime.InteropServices.Marshal.PtrToStringUni(ponteiro);
MessageBox.Show(" String decifrada : " + StringDecifrada);

 

O método Marshal.SecureStringToBSTR aloca um BSTR e copia o conteúdo de um objeto SecureString gerenciado para ele.

Nota: Um BSTR (Basic String or Binary String) é um tipo de dados que é usado por COM, automação e funções de interoperabilidade. A BSTR é um tipo de dados composto, que consiste de um prefixo de comprimento, uma seqüência de dados, e um terminador.

O coletor de lixo irá remover as SecureStrings quando elas não são mais referenciadas, mas você pode liberar uma SecureString usando o método Dispose():

secstr.Dispose();

Usando SecureString em um projeto Windows Forms

Vamos abrir o Visual Studio 2017 Community e criar um novo projeto do tipo Windows Forms Application com o nome CShp_SecureString;

No formulário Form1.cs inclua um controle Label, um TextBox (txtSenha) um Button (btnGerarStringSegura) e um TextBox (txtResultado, Multiline=True) conforme o leiaute abaixo:

Clique no menu PROJECT e a seguir em Add Class e informe o nome TratandoStringSegura.cs e a seguir inclua o código abaixo neste arquivo:

Imports System.Security

public Class GerarStringSegura
{
  'metodo para converter uma senha usando securestring
   public SecureString ConverterParaSecureString(string strSenha)
        {
            SecureString stringSegura = null;
            try
            {
                stringSegura = new SecureString();
                if ((strSenha.Length > 0))
                {
                    foreach (Char caractere in strSenha.ToCharArray())
                    {
                        stringSegura.AppendChar(caractere);
                    }
                }
                return stringSegura;
            }
            catch
            {
                throw;
            }
        }
   }

Temos acima o código do método estático ConverterParaSecureString da classe TratandoStringSegura . Este método recebe uma string e a converte para SecureString. Vamos entendê-lo;

- Usamos o namespace System.Security que contém a classe SecureString();

- Criamos uma instância da classe SecureString():  stringSegura = new SecureString();

- Se o valor do argumento recebido for uma string com tamanho maior que zero então percorremos cada caractere da string e usando o método AppendChar() incluímos o caractere no objeto SecureString:

                if ((strSenha.Length > 0))
                {
                    foreach (Char caractere in strSenha.ToCharArray())
                    {
                        stringSegura.AppendChar(caractere);
                    }
                }

retornamos o objeto SecureString: return stringSegura;

Vamos voltar para o nosso projeto Windows Forms e usar esse método para converter uma string para SecureString e observar os resultados.

No evento Click do botão - Gerar String Segura - inclua o código abaixo:

        private void btnGerarStringSegura_Click(object sender, EventArgs e)
        {
            SecureString senhaSegura = null;
            if (string.IsNullOrEmpty(txtSenha.Text))
            {
                MessageBox.Show("Informe a senha");
            }
            else
            {
                try
                {
                    string strSenha = txtSenha.Text;
                    senhaSegura = TratandoStringSegura.ConverterParaSecureString(txtSenha.Text);
                    txtResultado.Text = txtResultado.Text + "Senha digitada (armazenada na variavel string) : " 
+ strSenha.ToString() + Environment.NewLine + Environment.NewLine;
                    txtResultado.Text = txtResultado.Text + "Senha Convertida para SecureString : " + senhaSegura.ToString()
 + Environment.NewLine + Environment.NewLine;
                    strSenha = string.Empty;
                    txtResultado.Text = txtResultado.Text + "Senha atual Limpa : " + strSenha.ToString() + Environment.NewLine;
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Erro : " + ex.Message);
                }
            }
        }

Este código cria um objeto SecureString() - senhaSegura, e uma string - strSenha.

Exibimos o valor da senha digitada na string: strSenha.ToString() e armazenamos a senha digitada na string strSenha.

A seguir usando o método estático ConverterParaSecureString() convertemos a string para SecureString() com o mesmo conteúdo e armazenamos em senhaSegura.

Tentamos exibir o valor da SecureString() : senhaSegura.ToString()

Execute o projeto, informe uma senha e clique no botão - Gerar String Segura. Você vai obter o seguinte resultado:

Observe que não conseguimos acessar o conteúdo da SecureString.

Obtendo o valor da SecureString

Como podemos então acessar o conteúdo da SecureString ???

Vamos criar um método na classe TratandoStringSegura para fazer esse trabalho.

Inclua o método LerSecureString nesta classe com o código a seguir:

      public static string LerSecureString(SecureString secure)
        {
            IntPtr unmanagedPtr = IntPtr.Zero;
            try
            {
                if (secure == null)
                    throw new ArgumentNullException("Senha não definida");
                unmanagedPtr =  Marshal.SecureStringToGlobalAllocUnicode(secure);
                return Marshal.PtrToStringUni(unmanagedPtr);
            }
            catch 
            {
                throw;
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedPtr);
                secure.Dispose();
            }
        }

Este código acessa o conteúdo de uma SecureString.

O método Marshal.SecureStringToGlobalAllocUnicode(secure) copia o conteúdo de um objeto SecureString() gerenciado para a memória não gerenciada.

O método Marshal.PtrToStringUni aloca uma string gerenciada e copia todos os caracteres até o primeiro caractere nulo(null) de uma string Unicode não gerenciada para ela.

Como esse método aloca a memória não gerenciada para uma string, temos que liberar a memória chamando o método ZeroFreeGlobalAllocUnicode.

Agora vamos incluir as linhas de código abaixo no final do código do evento Click do botão - Gerar String Segura :


strSenha = TratandoStringSegura.LerSecureString(senhaSegura);
txtResultado.Text = txtResultado.Text + "SecureString convertida para String : " + strSenha.ToString() + Environment.NewLine;
 

Executando novamente o projeto iremos obter:

E assim conseguimos acessar o conteúdo da SecureString().

Pegue o projeto completo aqui: CShp_SecureString.zip

(disse Jesus) 'Todavia digo-vos a verdade, que vos convém que eu vá; porque, se eu não for, o Consolador não virá a vós; mas, quando eu for, vo-lo enviarei.'
João 16:7

Referências:


José Carlos Macoratti