VB .NET - Criando código mais seguro


Quando você cria um aplicação não deve negligenciar um item fundamental mas muitas vezes deixado em segundo plano : a proteção do código , configurações e dados envolvidos na aplicação.

A proteção do seu código fonte assegura que seu trabalho intelectual não seja roubado a proteção dos dados tratados pela sua aplicação também é fundamental afinal você é o responsável pela aplicação e seu cliente vai querer que os dados que ela trate estejam seguros.

Neste artigo eu vou procurar dar em linhas gerais alguns procedimentos para tratar da proteção de uma aplicação :  código fonte , dados e configuração. Quero entretanto deixar claro que você não deve tomar este artigo como a palavra final em segurança para aplicações . Ao contrário , ele é apenas uma introdução básica neste vasto e intrincado assunto muito em moda nos dias atuais :  segurança.

1- Proteção de senhas e nomes de usuários

Nunca armazene senhas e nomes de usuários na forma de texto aberto. Aonde quer que você for armazenar senhas e chaves de usuários faça isto de forma mascarada via criptografia.  Assim se alguém tiver acesso as senhas e nomes de usuários ele terá que ter o trabalho de decodificar a informação antes de usar. O VB .NET oferece recursos fáceis de usar para que este serviço seja feito.

Abaixo um exemplo onde um Hash é gerado para uma string informada:

Imports System.Security.Cryptography

Imports System.Text

 

Module Module1

 

Sub Main()

 

Dim SHA1HASHValue() As Byte

Console.WriteLine("SHA1 HASH GENERATOR")

Console.WriteLine("Entre com o texto a criptografar :")

Dim sString As String = Console.ReadLine()

'Cria uma nova instância de UnicodeEncoding para 'converter a string em um array de bytes Unicode

Dim UE As New UnicodeEncoding()

'Converte o stringem um array de bytes.

Dim MessageBytes As Byte() = UE.GetBytes(sString)

'Cria uma instância de SHA1Managed para criar um valor  hash

Dim SHhash As New SHA1Managed()

'Cria o valor  hash

SHA1HASHValue = SHhash.ComputeHash(MessageBytes)

'Exibe o valor hash value no console.

Dim b As Byte

 

For Each b In SHA1HASHValue

    Console.Write("{0} ", b)

Next b

 

Console.ReadLine()

End Sub

End Module

O Hash gerado é um valor único de tamanho fixo representando uma quantidade de informação. Para o caso de senhas e chaves , se duas senhas ou chaves forem iguais elas irão gerar o mesmo Hash. Seu uso mais comum é nas assinaturas digitais e na integridade de dados.

Outra classe que pode ser usada para o mesmo objetivo é a classe  - FormsAuthentication Class - que fornece métodos estáticos que podemos usar para gerenciar a autenticidade da informação. Seus membros principais relacionados com a segurança  são:

Authenticate Tenta validar a credencial contra aquela contida na credencial configurada na armazenagem.
Decrypt Retorna uma instância de uma classe FormsAuthenticationTicket , gerando um tiquete de  autenticação criptografada de um cookie HTTP.
Encrypt Produz uma string contento um tiquete de autenticação criptografado adequado para usar com um cookie HTTP.
Equals (inherited from Object) Determina se dois objetos de instância são iguais.
HashPasswordForStoringInConfigFile Dada uma senha e uma string de identificando o tipo de hash gera uma senha hash adequada para ser armazenada no arquivo de configuração.

Vamos criar um projeto que mostrar como gerar um hash com base em dois algoritmos : SHA1 e MD5.

Inicie um novo projeto no VS.NET do tipo Visual Basic modelo Windows e no formulário padrão inclua os controles conforme figura abaixo:

- O objetivo é gerar um hash para uma senha informada

- Temos a opção de fazer a geração usando os algoritmos SHA1 ou MD5.

O código associado ao evento - Click - do botão - Gerar Hash da Senha - é :

Private Sub GenerateHashButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles GenerateHashButton.Click

 

Const SenhaVazia As String = "A informação da senha é obrigatória"

Const SenhaNaoConfere As String = "A senha não confere"

Const SenhaComBrancos As String = "A senha não pode iniciar nem terminar com espaços."

 

HashedPasswordTextBox.Text = ""

 

'verifica se as senhas informadas estão vazias.

If PasswordTextBox.Text.Trim() = [String].Empty Or ConfirmPasswordTextBox.Text.Trim() = [String].Empty Then

   MessageBox.Show(SenhaVazia)

   PasswordTextBox.SelectAll()

   PasswordTextBox.Focus()

   Return

End If

 

'verifica se não espaços em branco no inicio e no final da senha informada

If PasswordTextBox.Text.StartsWith(" ") Or PasswordTextBox.Text.EndsWith(" ") Then

   MessageBox.Show(SenhaComBrancos)

   PasswordTextBox.SelectAll()

   PasswordTextBox.Focus()

   Return

End If

 

'verifica se as senhas são iguais

If PasswordTextBox.Text <> ConfirmPasswordTextBox.Text Then

  MessageBox.Show(SenhaNaoConfere)

  PasswordTextBox.SelectAll()

  PasswordTextBox.Focus()

  Return

End If

 

'gera o hash e exibe na caixa de texto - HashedPasswordTextBox

HashedPasswordTextBox.Text = FormsAuthentication.HashPasswordForStoringInConfigFile(PasswordTextBox.Text, HashingAlgorithmName)

 

If ClipboardCopyCheckBox.Checked Then

   Clipboard.SetDataObject(HashedPasswordTextBox.Text, False)

End If

Return

 

End Sub

A linha de código responsável pelo serviço é dada a seguir , o restante do código é somente validação:

HashedPasswordTextBox.Text = FormsAuthentication.HashPasswordForStoringInConfigFile(PasswordTextBox.Text, HashingAlgorithmName)

 

 

Nota: Outra forma de gerar um hash MD5 e usando o seguinte código:

 

Function HashMD5(Senha as string) as string

Dim md5Hasher As New System.Security.Cryptography.MD5CryptoServiceProvider()

Dim hashedBytes As Byte()

Dim encoder As New System.Text.UTF8Encoding()

Dim saida As String

Dim b As Byte

 

hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(Senha))

 

For Each y In hashedBytes

   saida &= y

Next y

 

HashMD5 = saidsa

 

End Function

Podemos ainda usar o código a seguir . Para isto crie um novo projeto no VS.NET e no formulário padrão monte o layout conforme abaixo.

- No formulário basta instânciar a classe e usar :

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

 

Dim cifrado As New ClsCrypto()

txtCifrado.Text = cifrado.clsCrypto(txtTexto.Text, True)

 

End Sub


Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

 

Dim decifrado As New ClsCrypto()

txtDecifrado.Text = decifrado.clsCrypto(txtCifrado.Text, False)

 

End Sub

 

No menu Project selecione Add Class nomeando o arquivo como - clsCrypto.vb. Abaixo o código da classe - clsCrypto.vb.( Perceba que teremos acesso apenas ao método clsCrypto que é público.)

Imports System.Security.Cryptography
Imports System.Text

Public Class ClsCrypto
Dim myKey As String
Dim des As New TripleDESCryptoServiceProvider()
Dim hashmd5 As New MD5CryptoServiceProvider()

Public Sub New()
   'Inserir o codigo de configuração da classe
  
myKey = "Macoratti"
End Sub

Public Function clsCrypto(ByVal texto As String, ByVal Operacao As Boolean) As String
  If Operacao Then
    clsCrypto = Cifra(texto)
  Else
    clsCrypto = DeCifra(texto)
  End If
End Function

Private Function DeCifra(ByVal texto As String) As String
 

  DES.Key = hashmd5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(myKey))
  DES.Mode = CipherMode.ECB
  Dim desdencrypt As ICryptoTransform = DES.CreateDecryptor()
  Dim buff() As Byte = Convert.FromBase64String(texto)
  DeCifra = ASCIIEncoding.ASCII.GetString(desdencrypt.TransformFinalBlock(buff, 0,buff.Length))


End Function

Private Function Cifra(ByVal texto As String) As String
  des.Key = hashmd5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(myKey))
  des.Mode = CipherMode.ECB
  Dim desdencrypt As ICryptoTransform = des.CreateEncryptor()
  Dim MyASCIIEncoding = New ASCIIEncoding()
  Dim buff() As Byte = ASCIIEncoding.ASCII.GetBytes(texto)
  Cifra = Convert.ToBase64String(desdencrypt.TransformFinalBlock(buff,0, buff.Length))
End Function

End Class

Com os códigos mostrados acima você tem uma forma de proteger , ainda que parcialmente , as senhas e nomes dos usuários. É claro que outra etapa seria proteger o local onde você esta armazenando esta informação.

Eu nem estou considerando que você vá guardar chave e senha no seu código apenas pensando que por esta compilado eles vão estar protegidos. Qualquer usuário com as ferramentas adequadas pode  decompilar (Disassembling) o seu código e você terá senha e nomes expostos.

Protegendo seu código da decompilação

Quando você compila seu código-fonte C# ou VB.NET o compilador vai convertê-lo para uma linguagem intermediária chamada - MSIL - Microsoft Intermediate Language - que é um conjunto de instruções em um tipo de assembly independente da CPU. Ao gerar o MSIL o compilador também gera metadados que são as informações embutidas no seu projeto que descrevem os tipos e suas definições , as assinaturas dos membros de cada tipo e outros dados.

Nota: Se você conhece Java já sacou o conceito. Afinal em java o código compilado gera um bytecode que pode ser executado em qualquer plataforma que possua uma máquina virtual - JVM .

Abaixo temos um esquema representando esta operação , a compilação e geração de código MSIL e a decompilação e geração de código C# :

A MSIL possui uma vasta gama de instruções para carregar , armazenar , inicializar , chamar métodos em objetos , operações lógicas , controles de fluxo , acesso direto a memória e manipulação de exceções. Antes do código poder ser executado a MSIL precisa ser convertida para o código da CPU do ambiente em que vai rodar ; isto é feito por compiladores Just in time (JIT) que converte o código para assembly nativo.

No final do processo você terá um arquivo PE (Portable Executable) que contém a MSIL e os metadados que irá rodar na CLR - Common Language RunTime. O problema é que , como foi abordado no artigo - Decompilando seu projeto VB.NET  , o seu código pode ser decompilado usando ferramentas apropriadas.

Você não esta acreditando muito nesta história ? Pois vou mostrar que usando uma ferramenta da própria Microsoft -  ISDASM.EXE - podemos expor o seu código fonte.  Crie a seguinte classe em um projeto do tipo Console.

Class1.vb

imports system

   Namespace Macoratti

    class m

      shared sub main
         console.writeline("Olá , Eu estou aqui em sub main")
      end sub

      public function Saudacoes as string
         Saudacoes = "Ola , eu estou na função Saudacoes"
      end function

    end class

  end namespace
 

Após compilar este código teremos um arquivo executável de nome class1.exe. Abaixo uma figura onde estou exibindo as etapas seguidas para gerar e testar o arquivo class1.exe.

As etapas seguidas para gerar e testar o arquivo class1.exe

- O arquivo class1.vb criado no bloco de notas

- a compilação do arquivo class1.vb : vbc class1.vb

- o arquivo class1.exe gerado

- a execução do arquivo class1.exe exibindo a mensagem

Vamos agora usar o utilitário ILDASM para obter o código IL o qual é lido a partir do METADATA do assembly. Para isto basta digitar a seguinte linha de comando:

ILDASM Class1.exe /out=Class1.il

Agora você pode dar uma espiada no arquivo class1.il gerado e poderá ver muito mais do que apenas bytes...

Você pode executar o utilitário ILDASM a partir do Windows . Ele geralmente esta na basta bin de FrameworkSDK na pasta onde você instalou o VS.NET. Veja abaixo a figura do ildasm.exe exibindo a class1.exe

Para obter mais detalhes sobre o código clique duas vezes sobre o item. Abaixo a figura exibindo detalhes do método main:

Acredita agora ??? Se com um simples utilitário Microsoft você conseguir tudo isto imagine com uma ferramenta especializada ????

Então eu pergunto : Existe alguma forma de proteção do código compilado sem ter que usar e pagar por ofuscadores de código de terceiros ?

Sim , existe uma forma de dificultar o processo.  Você deve seguir os seguintes passos após gerar o seu executável - class1.exe.

  1. Gerar o IL
ILDASM class1.exe /out=class1.il
  1. Definir a opção : owner
ILASM /owner=mac class1.il

Desta forma o arquivo class1.exe criado com a chave owner=mac não poderá ser aberto usando o utilitário ILDASM , se alguém tentar vai obter a mensagem :  "Copyrighted Material- can not disassemble"

Para conseguir é necessário saber a chave definida com owner :

ILDASM /owner=mac Class1.exe

É claro que isto não vai proteger o seu código de forma definitiva mas já coloca uma barreira a mais a ser superada.

Protegendo arquivos UDL

Sinceramente eu não uso arquivos UDL para fornecer dados para a string de  conexão. Um arquivo UDL é um recurso externo a mais que você vai ter que proteger em sua aplicação . Se você estiver operando sob o NTFS pode até atribuir uma proteção de permissão de acesso a arquivo. Se não estiver , vai ter que procurar uma forma de esconder a informação do arquivo.

Mantenha - Persist Security Info - como False

Se você definir - Persist Security Info - como true ou yes , irá permitir que a chave e senha possam ser obtidas da conexão depois que a mesma for aberta. Se você estiver fornecendo chave e senha para abrir uma conexão esta informação deve ser usada e logo descartada. Para que isto ocorra defina - Persist Security Info - como False ou no.

Cuidado com strings de conexão criadas com entradas de usuário

Se você monta sua string de conexão a partir de uma informação fornecida pelo usuário ( senha e chave) você deve ter cuidado para ter certeza de que os valores usados para criar sua string de conexão não contenham informação extra que possam alterar o comportamento de sua conexão. Procure efetuar a validação da entrada do usuário verificando se o formato da string de conexão não será afetado.

As expressões regulares podem ser usadas para validar entradas de dados em um formato específico. O .NET Framework fornece o objeto Regex para validar um valor contra uma expressão. Para verificar se o valor de uma chave de usuário tem 8 caracteres alfanuméricos do tipo string podemos usar o código:

Imports System.Text.RegularExpressions
Public Static Function ValidarUsuario(inString As String) As Boolean
  Dim r As Regex = New Regex("^[A-Za-z0-9]{8}$")
  Return r.IsMatch(inString)
End Function

Até mais ...

Jo 5:24 Em verdade, em verdade vos digo que quem ouve a minha palavra, e crê naquele que me enviou, tem a vida eterna e não entra em juízo, mas já passou da morte para a vida.

João 5:25 Em verdade, em verdade vos digo que vem a hora, e agora é, em que os mortos ouvirão a voz do Filho de Deus, e os que a ouvirem viverão.

João 5:26 Pois assim como o Pai tem vida em si mesmo, assim também deu ao Filho ter vida em si mesmos;

João 5:27 e deu-lhe autoridade para julgar, porque é o Filho do homem.

 

Veja os Destaques e novidades do SUPER DVD VB  (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Veja mais sistemas completos para a plataforma .NET no Super DVD .NET , confira...

Quer aprender C# ??

Chegou o Super DVD C#  com exclusivo material de suporte e vídeo aulas com curso básico sobre C#.

      Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti