VB .NET - Protegendo a informação confidencial com a classe SecureString


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:

 Dim secString As New System.Security.SecureString()
        secString.AppendChar("M"c)
        secString.AppendChar("a"c)
        secString.AppendChar("c"c)
        secString.AppendChar("o"c)
        secString.AppendChar("r"c)
        secString.AppendChar("a"c)
        secString.AppendChar("t"c)
        secString.AppendChar("t"c)
        secString.AppendChar("i"c)

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

secString.MakeReadOnly()

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

Dim ponteiro As IntPtr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(secString)
Dim StringDecifrada As String = System.Runtime.InteropServices.Marshal.PtrToStringUni(ponteiro)

txtMsg.Text = " 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():

secString.Dispose()

Usando SecureString com VB .NET

Vamos abrir o Visual Studio 2012 Express for desktop e criar um novo projeto do tipo Windows Forms Application com o nome Usando_SegureString;

No formulário form1.vb inclua um controle Label, um TextBox (txtSenha) um Button (btnGerar) e um TextBox (txtMsg, Multiline=True) conforme o leiaute abaixo:

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

Imports System.Security

Public Class GerarStringSegura

    'metodo para converter uma senha usando securestring

    Public Function ConverterParaSecureString(strSenha As String) As SecureString
        Dim stringSegura = New SecureString()
        If strSenha.Length > 0 Then
            For Each c In strSenha.ToCharArray()
                stringSegura.AppendChar(c)
            Next
        End If
        Return stringSegura
    End Function

End Class

Temos acima o código do método ConverterParaSecureString da classe GerarStringSegura. 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(): Dim 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 Then
            For Each c In strSenha.ToCharArray()
                stringSegura.AppendChar(c)
            Next
 End If

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 Sub btnGerar_Click(sender As Object, e As EventArgs) Handles btnGerar.Click

        If String.IsNullOrEmpty(txtSenha.Text) Then
            MessageBox.Show("Digite a sua senha...", "Digite a senha", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Return
        End If

        'define um objeto SecureString e uma String
        Dim senhaSegura As New SecureString()
        Dim strSenha As String = txtSenha.Text

        txtMsg.Text = txtMsg.Text & "Senha digitada (armazenada na variavel string) : " & strSenha.ToString() & vbCrLf & vbCrLf

        'cria uma instância da classe GerarStringSegura
        Dim oStringSegura As New GerarStringSegura
     
        'Converte a string para SecureString
        senhaSegura = oStringSegura.ConverterParaSecureString(strSenha)

        'Limpa a variavel strSenha
        strSenha = ""

        'Confirma a conversão e tenta retornar a informação da SecureString
        txtMsg.Text = txtMsg.Text & "Senha Convertida para SecureString : " & senhaSegura.ToString() & vbCrLf & vbCrLf
        txtMsg.Text = txtMsg.Text & "Senha atual Limpa : " & strSenha.ToString() & vbCrLf

    End Sub

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

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

Armazenamos a senha digitada na string StrSenha e usando o método ConverterParaSecureString() convertemos a string para SecureString() com o mesmo conteúdo.

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 Conversor para fazer esse trabalho.

Inclua o método ConverteParaStringNaoSegura na classe Conversor com o código a seguir:

 Public Function ConverteParaStringNaoSegura(senhaSegura As SecureString) As String
        If senhaSegura Is Nothing Then
            Throw New ArgumentNullException("senhaSegura")
        End If

        Dim StringNaoGerenciada As IntPtr = IntPtr.Zero
        Try
            StringNaoGerenciada = Marshal.SecureStringToGlobalAllocUnicode(senhaSegura)
            Return Marshal.PtrToStringUni(StringNaoGerenciada)
        Finally
            Marshal.ZeroFreeGlobalAllocUnicode(StringNaoGerenciada)
        End Try
    End Function

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

O método Marshal.SecureStringToGlobalAllocUnicode(senhaSegura) 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 :

'converte a SecureString para uma string
strSenha = oConverte.ConverteParaStringNaoSegura(senhaSegura)

txtMsg.Text = txtMsg.Text & "SecureString convertida para String : " & strSenha.ToString() & vbCrLf

Executando novamente o projeto iremos obter:

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

Pegue o projeto completo aqui: usando_SecureString.zip

Mat 7:13 Entrai pela porta estreita; porque larga é a porta, e espaçoso o caminho que conduz à perdição, e muitos são os que entram por ela;

Mat 7:14 e porque estreita é a porta, e apertado o caminho que conduz à vida, e poucos são os que a encontram.

             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti