Conteúdo apresentado
Este conteúdo está desatualizado e não está mais sendo mantido. É fornecido como uma cortesia para indivíduos que ainda estão usando essas tecnologias. Esta página pode conter URLs que eram válidos quando originalmente publicados, mas agora vinculam-se a sites ou páginas que não existem mais

image

padrões e práticas Developer Center

J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Jason Taylor, Rudolph Araujo

Microsoft Corporation

outubro de 2005

Aplica-se a

  • Código Gerenciado (.NET Framework 2.0)

Resumo

Use as perguntas neste módulo para ajudá-lo a executar revisões de código de segurança em aplicativos de código gerenciado (.NET Framework 2.0). Use esta lista de perguntas em conjunto com o módulo, “Como: Executar uma revisão de código de segurança para código gerenciado (.NET Framework 2.0).”

Conteúdo

  • Como usar este módulo
  • O que há de novo em 2.0
  • Injeção SQL
  • Script entre sites
  • Validação de entrada/dados
  • Segurança de acesso ao código
  • Gerenciamento de Exceções
  • Representação
  • Dados sensíveis
  • Criptografia
  • Código inseguro
  • APIs não gerenciadas potencialmente perigosas
  • Auditoria e registro
  • Recursos Adicionais Multi-threading

Como usar este módulo

Use este módulo para realizar uma revisão de código eficaz para segurança. Cada categoria de perguntas inclui uma tabela que corresponde às vulnerabilidades a implicações e um conjunto de perguntas que você pode usar para determinar se seu aplicativo é suscetível às vulnerabilidades listadas. Uma referência que corresponda a vulnerabilidades a perguntas pode ser encontrada na seção “Matriz de vulnerabilidade/pergunta”.

Quando você usar este módulo, tenha em mente o seguinte:

  • Como executar a revisão do código de segurança para perguntas sobre código gerenciado (.NET Framework 2.0). Use o módulo companheiro, “Como: Execute uma revisão de código de segurança para código gerenciado (.NET Framework 2.0)”, para ajudá-lo a entender o processo de revisão de código.
  • Como usar a lista de perguntas. Use esta lista de perguntas como ponto de partida para “Passo 3. Código de revisão para problemas de segurança”, em “Como: Executar uma revisão de código de segurança para código gerenciado (.NET Framework 2.0).”
  • Priorize perguntas para revisão. Você pode não precisar responder a todas as perguntas porque algumas podem não ser relevantes para sua aplicação.

O que há de novo em 2.0

Esta seção descreve as alterações mais importantes no .NET Framework 2.0 que você deve estar ciente quando executar uma revisão de código de segurança. As principais mudanças incluem:

Exceção de segurança. O objeto SecurityException foi aprimorado para fornecer mais informações no caso de uma permissão falha. Invólucro gerenciado pelo DPAPI. O .NET Framework 2.0 fornece um conjunto de classes gerenciadas para acessar a API (Win32 Data Protection API). Isso torna mais fácil proteger dados confidenciais na memória quando você escreve código gerenciado. Você não precisa mais usar P/Invoke. O código exige que a nova Permissão de Proteção de Dados seja capaz de usar o DPAPI. Criptografia XML. A classe EncryptedXML pode ser usada para proteger dados confidenciais, como strings de conexão de banco de dados, que devem ser armazenados em disco. SecureString. Este novo tipo usa DPAPI para garantir que segredos armazenados na forma de string não sejam expostos a ataques de memória ou cheiramento de disco. Use as seguintes perguntas para garantir que o código use corretamente os novos recursos do .NET Framework 2.0:

  • O código aproveita as melhorias no SecurityException?
  • O código usa DPAPI para proteger dados confidenciais na memória?
  • O código usa encryptedXML para armazenar dados confidenciais em disco?
  • O código garante que o SecureStrings não seja passado desnecessariamente como strings regulares?

O código aproveita as melhorias no SecurityException?

Se o aplicativo estiver sendo executado em um ambiente de confiança parcial, use o objeto SecurityException para lidar graciosamente com falhas na solicitação de permissão. A Tabela 1 mostra as propriedades deste objeto que facilitam a depuração de problemas de segurança.

Tabela 1. Propriedades do objeto SecurityException

Nome Tipo Descrição: ____
Ação Ação de Segurança A Ação de Segurança que falhou na verificação de segurança.
Exigiu Objeto Os conjuntos de permissão, permissão ou permissão que foram exigidos e desencadearam a exceção.
DenySetInstance Objeto Se um quadro de pilha de negação causou a falha da exceção de segurança, esta propriedade conterá esse conjunto; caso contrário, será nulo.
FailedAssemblyInfo Nome de montagem Nome de montagem do conjunto que causou a falha na verificação de segurança.
FirstPermissionThatFailed IPermission A primeira permissão na falha do PermissionSet (ou PermissionSetCollection) que não passou na verificação de segurança.
Método MétodoInfo O método em que a montagem falhou estava quando encontrou a verificação de segurança que acionou a exceção. Se um quadro de pilha permitironly ou negar falhar, isso conterá o método que colocou o quadro PermitOnly ou Deny na pilha.
PermissãoOnlySetInstance Objeto Se o quadro de pilha que causou a exceção de segurança tiver uma permissão de permissão, esta propriedade irá contê-la, caso contrário, será nula.
Url Corda URL do conjunto que falhou na verificação de segurança.
Zona Zona de Segurança Zona da montagem que falhou na verificação de segurança

O código usa DPAPI para proteger dados confidenciais na memória?

Se o aplicativo estiver manipulando dados confidenciais, ele deve usar o DPAPI para armazenar os dados em forma criptografada. Criptografar dados confidenciais até que sejam usados reduz as chances de que sejam roubados da memória. O DPAPI agora está acessível através da classe System.Security.Cryptography.ProtectedMemory.

O código usa encryptedXML para armazenar dados confidenciais em disco?

Se o aplicativo armazena informações confidenciais, como strings de conexão de banco de dados, em disco em formato XML, ele deve usar a classe System.Security.Cryptography.XML.EncryptedXML para proteger as informações. Criptografar informações confidenciais quando armazenadas em disco reduz as chances de que sejam roubadas.

O código garante que o SecureStrings não seja passado desnecessariamente como strings regulares?

O SecureString protege dados confidenciais somente se os dados nunca forem armazenados como uma sequência. Evite armazenar dados confidenciais como uma sequência, se possível, porque uma string é imutável (somente leitura). Cada vez que você altera o valor de uma string, o valor original da sequência é mantido na memória junto com o novo valor. Se dados confidenciais forem armazenados na memória como uma sequência e, em seguida, forem criptografados, a sequência original na memória ainda pode ser roubada. A melhor maneira de garantir que uma sequência de sequência na memória não possa ser roubada é garantir que ela seja armazenada apenas com SecureString.

Injeção SQL

Seu código é vulnerável a ataques de injeção SQL onde quer que ele use parâmetros de entrada para construir instruções SQL. Um ataque de injeção SQL ocorre quando uma entrada não confiável pode modificar a lógica de uma consulta SQL de maneiras inesperadas. Ao revisar o código, certifique-se de que qualquer entrada usada em uma consulta SQL seja validada ou que as consultas SQL sejam parametrizadas. A Tabela 2 resume a vulnerabilidade da injeção SQL e suas implicações.

Tabela 2: Vulnerabilidades e Implicações da Injeção SQL

Vulnerabilidade Implicações
Entrada não validada usada para gerar consultas SQL As injeções de SQL podem resultar em acesso, modificação ou destruição não autorizada de dados SQL.

As seguintes perguntas podem ajudá-lo a identificar áreas vulneráveis:

  • A aplicação é suscetível à injeção sql?
  • O código usa procedimentos armazenados parametrizados?
  • O código tenta filtrar a entrada?

A aplicação é suscetível à injeção sql?

Preste muita atenção ao seu código de acesso aos dados. Procure as strings “SqlCommand”, “OleDbCommand” ou “OdbcCommand” para ajudar a identificar o código de acesso aos dados. Identifique qualquer campo de entrada que você use para formar uma consulta de banco de dados SQL. Verifique se esses campos são validados adequadamente para tipo, formato, comprimento e alcance.

O código usa procedimentos armazenados parametrizados?

Os procedimentos armazenados sozinhos não podem evitar ataques de injeção SQL. Verifique se seu código usa procedimentos armazenados parametrizados. Verifique se seu código usa objetos de parâmetro digitado, como SqlParameter, OleDbParameter ou OdbcParameter. O exemplo a seguir mostra o uso de um SqlParameter.

SqlDataAdapter myCommand = new SqlDataAdapter("spLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
                                "@userName", SqlDbType.VarChar,12);
parm.Value=txtUid.Text;

O parâmetro SQL digitado verifica o tipo e o comprimento da entrada e garante que o valor de entrada do UserName seja tratado como um valor literal e não como código executável no banco de dados.

O código usa parâmetros em declarações SQL?

Se o código não utilizar procedimentos armazenados, certifique-se de que ele usa parâmetros nas instruções SQL que ele constrói, como mostrado no exemplo a seguir.

select status from Users where UserName=@userName

Verifique se o código não usa a seguinte abordagem, onde a entrada é usada diretamente para construir a instrução SQL executável usando concatenação de sequência.

string sql = "select status from Users where UserName='"
           + txtUserName.Text + "'";
           

O código tenta filtrar a entrada?

Uma abordagem comum é desenvolver rotinas de filtro para adicionar personagens de fuga a personagens que têm significado especial ao SQL. Esta é uma abordagem insegura, e os desenvolvedores não devem confiar nela por causa de problemas de representação de caracteres.

Scripting entre sites

O código é vulnerável a ataques de scripting entre sites onde quer que ele use parâmetros de entrada no fluxo HTML de saída devolvido ao cliente. Mesmo antes de realizar uma revisão de código, você pode executar um teste simples para determinar se seu aplicativo está vulnerável. Pesquise páginas onde as informações de entrada do usuário são enviadas de volta para o navegador.

Para realizar este teste, digite texto, como XYZ, em campos de formulário e teste a saída. Se o navegador exibir XYZ ou se você ver XYZ quando você visualizar a fonte HTML, então seu aplicativo da Web será vulnerável ao scripting entre sites. Se você quiser realizar um teste mais dinâmico, injete <script>alert('olá'); </script>. Essa técnica pode não funcionar em todos os casos porque depende de como a entrada é usada para gerar a saída.

A Tabela 3 resume a vulnerabilidade de scripting entre sites e suas implicações.

Tabela 3: Vulnerabilidade e implicações do scripting entre sites

Vulnerabilidade Implicações
Entrada não validada e não confiável no fluxo de saída HTML O script entre sites pode permitir que um invasor execute um script malicioso ou roube a sessão e/ou cookies de um usuário.

As seguintes perguntas podem ajudá-lo a identificar áreas vulneráveis:

  • O código ecoa a entrada do usuário ou os parâmetros de URL de volta a uma página da Web?
  • O código persiste a entrada do usuário ou os parâmetros de URL para um armazenamento de dados que poderiam ser exibidos posteriormente em uma página da Web?

O código ecoa a entrada do usuário ou os parâmetros de URL de volta a uma página da Web?

Se você incluir parâmetros de entrada do usuário ou URL no fluxo de saída HTML, você pode estar vulnerável ao scripting entre sites. Certifique-se de que o código valida a entrada e que ele usa HtmlEncode ou UrlEncode para validar a saída. Mesmo que um usuário mal-intencionado não possa usar a interface do usuário do seu aplicativo para acessar parâmetros de URL, o invasor ainda pode ser capaz de adulterá-los.

O script de local cruzado reflexivo é menos perigoso do que o script de cross-site persistente devido à sua natureza transitória.

O aplicativo não deve conter código semelhante ao exemplo a seguir.

Response.Write( Request.Form["name"] );

Em vez disso, o aplicativo deve conter código semelhante ao seguinte.

Response.Write( HttpUtility.HtmlEncode( Request.Form["name"] ) );

O código persiste a entrada do usuário ou os parâmetros de URL para um armazenamento de dados que poderiam ser exibidos posteriormente em uma página da Web?

Se o código usar a vinculação de dados ou acesso explícito ao banco de dados para colocar os parâmetros de entrada do usuário ou URL em uma loja de dados persistente e, posteriormente, incluir esses dados no fluxo de saída HTML, o aplicativo pode ser vulnerável a scripting entre sites. Verifique se o aplicativo usa HtmlEncode ou UrlEncode para validar a saída de entrada e codificação. Preste especial atenção às áreas do aplicativo que permitem aos usuários modificar configurações ou configurações de personalização. Também preste atenção à entrada persistente de usuários de formulário livre, como quadros de mensagens, fóruns, discussões e postagens na Web. Mesmo que um invasor não possa usar a interface do usuário do aplicativo para acessar parâmetros de URL, um usuário mal-intencionado ainda pode ser capaz de adulterá-los.

O script persistente no local é mais perigoso do que o scripting cross-site reflexivo.

Validação de entradas/dados

Se você fizer suposições infundadas sobre o tipo, comprimento, formato ou alcance de entrada, é improvável que sua aplicação seja robusta. A validação de entrada pode se tornar um problema de segurança se um invasor descobrir que você fez suposições infundadas. Em seguida, o invasor pode fornecer entrada cuidadosamente trabalhada que compromete sua aplicação. A Tabela 4 mostra um conjunto de vulnerabilidades comuns de entrada e/ou validação de dados e suas implicações.

Tabela 4: Vulnerabilidades e implicações de validação de dados e entradas

Vulnerabilidade Implicações
Entrada não validada e não confiável no fluxo de saída HTML O aplicativo é suscetível a ataques de scripting entre sites.
Entrada não validada usada para gerar consultas SQL A aplicação é suscetível a ataques de injeção SQL.
Dependência da validação do lado do cliente A validação do cliente é facilmente ignorada.
Uso de nomes de arquivos de entrada, URLs ou nomes de usuários para decisões de segurança A aplicação é suscetível a problemas de canonização, o que pode levar a falhas de segurança.
Filtros somente aplicativos para entrada maliciosa Isso é quase impossível de fazer corretamente devido à enorme gama de entradas potencialmente maliciosas. O aplicativo deve restringir, rejeitar e higienizar a entrada.

Use as seguintes perguntas quando revisar a entrada e validação de dados do código:

  • O código valida dados de todas as fontes?
  • O código usa uma abordagem centralizada para validação de dados e entradas?
  • O código depende da validação do lado do cliente?
  • O código é suscetível a ataques de canonização?
  • O código é suscetível à injeção sql?
  • O código é suscetível ao scripting entre sites?

O código valida dados de todas as fontes?

Verifique se seu código não faz suposições sobre a validade dos dados de entrada. Deve assumir que todos os dados são maliciosos. Os aplicativos web devem validar dados de todas as fontes, incluindo campos de formulário, sequências de consulta, cookies e cabeçalhos HTTP.

O código usa uma abordagem centralizador para validação de dados e entradas?

Para tipos comuns de campos de entrada, examine se seu código usa ou não bibliotecas comuns de validação e filtragem para garantir que as regras de validação sejam executadas de forma consistente e que tenha um único ponto de manutenção.

O código depende da validação do lado do cliente?

A validação do lado do cliente pode reduzir o número de viagens de ida e volta para o servidor, mas não depende dele para segurança, pois é fácil de contornar. Valide toda a entrada no servidor.

É fácil modificar o comportamento do cliente ou apenas escrever um novo cliente que não observe as mesmas regras de validação de dados. Considere o exemplo a seguir.

<html>
<head>
<script language='javascript'>
function validateAndSubmit(form)
{
   if(form.elments["path"].value.length() > 0)
   {
      form.submit();
   }
}
</script>
</head>
<body>
<form action="Default.aspx" method="post">
<input type=text id=path/>
<input type=button onclick="javascript:validateAndSubmit(this.parent)" Value="Submit" />
</form>
</body>
</html>

Neste exemplo, o scripting do lado do cliente valida que o comprimento do “caminho” é maior que zero. Se o processamento desse servidor depende dessa suposição para mitigar uma ameaça à segurança, então o invasor pode facilmente quebrar o sistema.

O código é suscetível a ataques de canonização?

Erros de canonização ocorrem sempre que há várias maneiras de representar um recurso, e as diferentes representações resultam em uma lógica de segurança variada sendo executada. Existem vários tipos de recursos para os quais esse problema pode ocorrer, incluindo:

  • Recursos de arquivo
    • O uso de caminhos parciais pode resultar em um arquivo diferente do que você espera ser carregado.
    • O uso da variável ambiente PATH pode dar controle dos caminhos que seu aplicativo usa para um invasor.
  • URLs
    • A representação alternativa de um endereço IP, como IP sem ponto, pode resultar em uma URL diferente do que você esperava ser carregado.
    • Caracteres codificados, como %20 para espaço, podem resultar em uma URL diferente do que você esperava ser carregado.

O resultado desta questão é que um invasor ganha acesso a um recurso ao qual não teria acesso de outra forma. Ao revisar o código, olhe atentamente para áreas onde os recursos são acessados com base na entrada do usuário. Certifique-se de que os nomes dos arquivos sejam canonizados antes de serem usados com Path.GetFullPath. Certifique-se de que os URLs são canonizados antes de serem usados com Uri.AbsoluteUri.

Considere usar a segurança de acesso ao código para uma camada extra de proteção. Recuse permissões que não são necessárias e indique para o tempo de execução quais permissões seu código precisa, como mostrado no exemplo a seguir.

[assembly:FileIOPermission( SecurityAction.RequestMinimum, Read = "c:\\temp" )]
[assembly:FileDialogPermission( SecurityAction.RequestOptional )]
[assembly:FileIOPermission( SecurityAction.Deny, Write = "c:\\windows" )]

O código é suscetível à injeção sql?

Seu código é vulnerável a ataques de injeção SQL se ele usar parâmetros de entrada para construir instruções SQL. Um ataque de injeção SQL ocorre quando uma entrada não confiável pode modificar a semântica de uma consulta SQL de maneiras inesperadas. À medida que você está revisando o código, certifique-se de que as consultas SQL estão parametrizadas e que qualquer entrada usada em uma consulta SQL seja validada.

Considere o seguinte exemplo de código de consulta SQL.

query = "SELECT * FROM USERS WHERE USER_ID = '" + userIdFromWebPage + "'";

userIdFromWebPage é uma variável que contém dados não confiáveis que não foram validados. Imagine que ele contém um dos seguintes:

  • ”’ ou 1=1 -“
  • ”’ ;D OSSOS DE TABELA DO ;D ROP -“
  • ”’ ;executivo xp_cmdshell(‘‘formato c:’) -“

A consulta final pode ser assim.

"select * FROM USERS WHERE USER_ID = '' ;exec xp_cmdshell('format c:') -"

Isso resulta em um formato do c:\ unidade no servidor de banco de dados.

O código deve usar parâmetros fortemente digitado e se parecer com este:

SqlCommand queryCMD = new SqlCommand("GetUser", sqlConn);
queryCMD.CommandType = CommandType.StoredProcedure;
SqlParameter myParm = queryCMD.Parameters.Add("@UserID", SqlDbType.Int, 4);
myParm.Value = userIdFromWebPage;

SqlDataReader myReader = queryCMD.ExecuteReader();

O código é suscetível ao scripting entre sites?

O scripting entre sites ocorre quando um invasor consegue inserir o código do script em um aplicativo para que ele seja ecoado de volta e executado no contexto de segurança do aplicativo. Isso permite que um invasor roube informações do usuário, incluindo dados de formulários e cookies. Essa vulnerabilidade pode estar presente sempre que um aplicativo da Web ecoa a entrada de usuário não filtrada de volta ao conteúdo da Web.

Ao revisar o código, certifique-se de que dados não confiáveis cuja saída final é o conteúdo da página da Web não contenham tags HTML. Os dados podem passar de entrada não confiável para saída de página da Web usando um caminho de rotatória — por exemplo, colocado em um banco de dados, recuperado do banco de dados e, em seguida, exibido em uma página da Web. Para proteger contra esse problema de segurança, certifique-se de que o HTMLEncode ou URLEncode sejam usados antes que a entrada do usuário seja ecoada de volta ao conteúdo da Web.

Segurança de acesso ao código

A segurança de acesso ao código é um modelo de restrição de recursos projetado para restringir os tipos de recursos do sistema que o código pode acessar e os tipos de operações privilegiadas que o código pode executar. Essas restrições são independentes do usuário que chama o código ou da conta de usuário sob a qual o código é executado. Se o código que você está revisando funcionar em ambientes parcialmente confiáveis e usar técnicas explícitas de segurança de acesso ao código, revise-o cuidadosamente para garantir que a segurança de acesso ao código seja usada adequadamente. A Tabela 5 mostra possíveis vulnerabilidades que ocorrem com o uso inadequado da segurança de acesso ao código.

Tabela 5: Vulnerabilidades e implicações de segurança de acesso ao código

Vulnerabilidade Implicações
Uso inadequado de demandas de link ou afirma O código é suscetível a atrair ataques.
Código permite chamadas não confiáveis O código malicioso pode usar o código para executar operações confidenciais e acessar recursos.

Se o código usar técnicas explícitas de segurança de acesso ao código, revise-o para o seguinte:

  • O código usa demandas de link ou afirma chamadas?
  • O código usa AllowPartiallyTrustedCallersAttribute?
  • O código usa permissões potencialmente perigosas?
  • O código dá muita confiança às dependências?

Olhe atentamente a cada chamada linkdemand e assert. Estes podem abrir o código para atrair ataques porque a caminhada da pilha de acesso ao código será interrompida antes de ser concluída. Embora seu uso às vezes seja necessário por razões de desempenho, certifique-se de que não pode haver chamadas não confiáveis mais altas na pilha que poderia usar a chamada LinkDemand ou Assert deste método como um mecanismo de ataque.

O código usa AllowPartiallyTrustedCallersAttribute?

Preste atenção especial se o código permitir que os chamadores parcialmente confiáveis, incluindo o seguinte atributo.

[assembly: AllowPartiallyTrustedCallersAttribute()]

Isso permite que o conjunto seja acessível a partir de um código de chamada que não é totalmente confiável. Se o código que você está revisando, então chamadas para um conjunto que não permite chamadas parcialmente confiáveis, um problema de segurança pode resultar.

O código usa permissões potencialmente perigosas?

Revise o código para solicitações de permissões potencialmente perigosas, tais como: UnmanagedCode, MemberAccess, SerializationFormatter, SkipVerification, ControlEvidence / ControlPolicy, ControlAppDomain, ControlDomainPolicy e SuppressUnmanagedCodeSecurityAttribute.

O exemplo de código a seguir usa SuppressUnmanagedCodeSecurityAttribute. Você deve sinalizar isso como uma prática potencialmente perigosa.

[DllImport("Crypt32.dll", SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
[SuppressUnmanagedCodeSecurity]
private static extern bool CryptProtectData(
     ref DATA_BLOB pDataIn, 
     String szDataDescr, 
     ref DATA_BLOB pOptionalEntropy,
     IntPtr pvReserved, 
     ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct, 
     int dwFlags, 
     ref DATA_BLOB pDataOut);
     

O código dá muita confiança às dependências?

Sem salvaguardas explícitas, um invasor pode enganar o código para carregar uma biblioteca maliciosa em vez de código confiável. Verifique se todos os conjuntos carregados são fortemente nomeados. Esta etapa garante que a adulteração não possa ocorrer. Sem nomes fortes, seu código poderia, sem saber, chamar código malicioso. O uso de bibliotecas de códigos nativos torna isso mais difícil de fazer; portanto, o código deve evitar confiar no código nativo implicitamente. Bibliotecas nativas podem ser verificadas com um hash ou um certificado. Além disso, você deve ter certeza de que todas as bibliotecas estão carregadas com um caminho completo para evitar ataques de canonização.

Verifique também se a assinatura de atraso está ativada. A assinatura de atraso é geralmente considerada como uma boa prática porque ajuda a proteger a confidencialidade da chave privada que será usada para a assinatura do componente. O código a seguir mostra como a assinatura de atraso é implementada.

[assembly:AssemblyDelaySignAttribute(true)]

Gerenciamento de exceções

O manuseio seguro de exceções pode ajudar a evitar que certos ataques de serviço em nível de aplicativo possam evitar que informações em nível de sistema úteis aos atacantes sejam devolvidas ao cliente. Por exemplo, sem o manuseio adequado de exceção, informações como detalhes do esquema do banco de dados, versões do sistema operacional, traços de pilha, nomes de arquivos e informações de caminho, sequências de consulta SQL e outras informações de valor para um invasor podem ser devolvidas ao cliente. A Tabela 6 mostra possíveis vulnerabilidades que podem resultar de uma má ou falta de gerenciamento de exceções e as implicações dessas vulnerabilidades.

Tabela 6: Vulnerabilidades e Implicações do Gerenciamento de Exceções

Vulnerabilidade Implicações
Falha ao usar o manuseio estruturado de exceção O aplicativo é mais suscetível a ataques de negação de serviço e falhas lógicas, que podem expor vulnerabilidades de segurança.
Revelando muita informação para o cliente Um invasor pode usar essas informações para ajudar a planejar e ajustar ataques subsequentes.

As seguintes perguntas ajudam a identificar áreas vulneráveis:

  • Existe verificação de erro adequada e consistente?
  • As mensagens de erro dão muita informação?
  • O aplicativo impede que detalhes confidenciais de exceção sejam devolvidos ao cliente?
  • O aplicativo lida com erros e condições de exceção no código?

Existe verificação de erro adequada e consistente?

Certifique-se de que o aplicativo usa blocos de tentativa/captura e verificação de valor de retorno de forma consistente. Procure por blocos vazios. Revisar o manuseio de erros toda vez que um conjunto é carregado dinamicamente; procure chamadas para System.Reflection.Assembly.Load. Certifique-se de que, se uma biblioteca contiver funcionalidade de segurança e não carregar, o código padrão para uma segurança mais alta.

Procure lugares onde a personificação ou privilégios elevados não sejam reduzidos quando uma exceção é lançada. Isso pode ocorrer por causa de um problema lógico — o bloco de captura não contém o código certo — ou por causa de um uso indevido sutil de um bloqueio final por um invasor.

Os filtros de exceção são executados antes do bloqueio final; portanto, eles poderiam resultar em execução de código malicioso no contexto do código privilegiado, em vez de no contexto parcialmente confiável em que deveria estar sendo executado.

A seguir, um exemplo de lógica de código ruim.

try
{
  ElevatePrivilege();
  // If ReadSecretFile throws an exception privileges will not be lowered
  ReadSecretFile();
  LowerPrivilege();
}
catch(FileException fe)
{
  ReportException();
}

As mensagens de erro dão muita informação?

Mensagens de erro devem ser úteis para o usuário médio sem dar informações que um invasor poderia usar para atacar o aplicativo. Certifique-se de que o código não dá pilhas de chamadas, linhas de código, caminhos de arquivos do servidor, nomes de banco de dados ou qualquer outra coisa interna ao aplicativo. Essas informações não são úteis para um usuário, mas podem ser muito úteis para um invasor.

Certifique-se de que páginas de erro personalizadas foram implementadas em ASP.NET aplicativos para evitar que dados confidenciais sejam revelados e para garantir que o rastreamento do aplicativo tenha sido desligado.

Revise cuidadosamente os caminhos de erro sensíveis à segurança. Por exemplo, o código deve evitar alterar mensagens de erro para diferentes caminhos de código de erro durante a autenticação do usuário. Um problema comum é exibir diferentes mensagens de erro para nomes de usuário inválidos com senhas inválidas e nomes de usuário válidos com senhas inválidas. Embora a diferença de erros possa ser sutil, o resultado dará aos atacantes informações que eles podem usar para comprometer o aplicativo.

O aplicativo impede que detalhes confidenciais de exceção sejam devolvidos ao cliente?

Não revele muita informação ao interlocutor. Detalhes de exceção podem incluir números de versão do sistema operacional e do .NET Framework, nomes de métodos, nomes de computador, instruções de comando SQL, strings de conexão e outros detalhes úteis a um invasor. Registre mensagens de erro detalhadas no registro de eventos e devolva mensagens de erro genéricas ao usuário final. Para isso, certifique-se de que o PersonalizadoErrorsMode esteja configurado como “Ligado” e que as páginas de erro personalizadas sejam configuradas para erros conhecidos e uma página de erro padrão com informações genéricas seja configurada para todos os erros desconhecidos. Um arquivo de configuração de amostra mostrando configuração apropriada é mostrado aqui.

  <system.web>
  ... 
  <customErrors mode="On" defaultRedirect="DefaultErrorPage.htm">
    <error statusCode="401" redirect="KnownError401.htm"/>
    <error statusCode="402" redirect="KnownError402.htm"/>
  </customErrors>.
  ...
</system.web>

Certifique-se também de que o rastreamento está desativado da seguinte forma.

  <system.web>
  ...
  <trace enabled="false"/>
  ...
</system.web>

O aplicativo lida com erros e condições de exceção no código?

Certifique-se de que as condições de erro sejam tratadas corretamente e que o aplicativo não dependa de exceções em vez de verificação de condições. Certifique-se de que as condições de erro apropriadas sejam verificadas, erros são registrados e mensagens fáceis de usar são exibidas pelo aplicativo. Aqui está um exemplo de código:

  if(SomeConditionFailed)
{
  //Error condition, log the error
  log("error Condition")
  Page.Redirect("Error.htm");
} 

//continue with normal execution
...

Certifique-se de que, quando você chama o código que pode aumentar as exceções, o código de chamada usa blocos de tentativa/captura, conforme mostrado abaixo para lidar adequadamente com quaisquer exceções levantadas. Use também blocos finalmente para garantir que quaisquer recursos em uso quando a exceção ocorrer sejam devidamente fechados ou liberados.

try
{
  // Call code that might throw exceptions. For example registry access, database access or file access etc
}
catch
{
  // Log the exception
  throw; //Propagate the exception to the caller if appropriate
}
finally
{
  // Ensure resources are released
}

Representação

Se o aplicativo usar a representação, certifique-se de que ele seja implementado corretamente. A Tabela 7 lista as vulnerabilidades de personificação e suas implicações de segurança.

Tabela 7: Vulnerabilidades e Implicações de Personificação

Vulnerabilidade Implicações
Revelando credenciais de conta de serviço para o cliente Um invasor poderia usar essas credenciais para atacar o servidor.
Código é executado com privilégios maiores do que o esperado Um invasor pode causar mais dano quando o código é executado com privilégios mais altos.

As seguintes perguntas podem ajudá-lo a identificar áreas vulneráveis:

  • O aplicativo usa credenciais de representação codificadas?
  • O aplicativo limpa corretamente quando usa personificação?

O aplicativo usa credenciais de representação codificadas?

Se o código se passar por uma conta de serviço, ele não deve passar credenciais codificadas para LogonUser. Se o código precisar de várias identidades para acessar uma série de recursos e serviços a jusante, ele deve usar a transição de protocolo do Microsoft Windows Server™ 2003 e o construtor do WindowsIdentity. Isso permite que o código crie um token windows que é dado apenas o nome principal de usuário de uma conta (UPN). Para acessar um recurso de rede, o código precisa de delegação. Para usar a delegação, seu servidor precisa ser configurado como confiável para delegação no serviço de diretório do Microsoft Active Directory®.

O código a seguir mostra como construir um token Windows usando o construtor Do WindowsIdentity.

  using System;
using System.Security.Principal;
public void ConstructToken(string upn, out WindowsPrincipal p)
{
  WindowsIdentity id = new WindowsIdentity(upn);
  p = new WindowsPrincipal(id);
}

O aplicativo limpa corretamente quando usa personificação?

Se o código usar personificação programática, verifique se ele usa manuseio de exceção estruturado e se o código de representação está dentro de blocos de tentativa. Certifique-se de que os blocos de captura são usados para lidar com exceções e que, finalmente, os blocos são usados para garantir que a representação seja revertida. Ao usar um bloco final, o código garante que o token de representação seja removido do segmento atual, sendo gerada ou não uma exceção.

O aplicativo não deve conter código semelhante ao seguinte:

  try
{  
  ElevatePrivilege();
  // if ReadSecretFile throws an exception privileges will not be lowered
  ReadSecretFile();
  LowerPrivilege()
}
catch(FileException fe)
{
  ReportException();
}

Em vez disso, deve conter código semelhante ao seguinte:

  try
{  
  ElevatePrivilege();
  // If ReadSecretFile throws an exception privileges will not be lowered
  ReadSecretFile();
}
catch(FileException fe)
{
  ReportException();
}
finally
{
  LowerPrivilege();
}

Dados confidenciais

Se o código que você está revisando usar dados confidenciais, como strings de conexão e credenciais de conta, você deve certificar-se de que o código protege os dados e garante que ele permaneça privado e sem uso. A Tabela 8 mostra um conjunto de vulnerabilidades de dados sensíveis comuns e suas implicações.

Tabela 8: Vulnerabilidades e Implicações de Dados Sensíveis

Vulnerabilidade Implicações
Armazenar segredos quando você não precisa Isso aumenta drasticamente o risco de segurança. Não guarde segredos desnecessariamente.
Armazenando segredos em código Se o código estiver no servidor, um invasor poderá baixá-lo. Segredos são visíveis em assembleias binárias.
Armazenamento de segredos em texto claro Qualquer um que possa fazer logon no servidor pode ver dados secretos.
Passando dados confidenciais em texto claro sobre redes Os bisbilhoteiros podem monitorar a rede para revelar e adulterar os dados.

As seguintes perguntas ajudam a identificar áreas vulneráveis:

  • Os segredos da loja de códigos?
  • Os dados confidenciais são armazenados em locais previsíveis?

Os segredos da loja de códigos?

Se uma montagem armazena segredos, revise o design para ter certeza de que é absolutamente necessário armazenar o segredo. Se o código deve armazenar um segredo, revise as seguintes perguntas para ter certeza de que ele o fará da forma mais segura possível:

O aplicativo armazena segredos em código?

Há segredos ou propriedade intelectual crítica embutidos no código? Código gerenciado é fácil de descompilar. É possível recuperar o código do executável final que é muito semelhante ao código original. Qualquer propriedade intelectual sensível ou segredos codificados podem ser roubados com facilidade. Um ofuscante pode dificultar esse tipo de roubo, mas não pode impedi-lo totalmente. Outro problema comum é usar campos de formulário ocultos pensando que essas informações não serão visíveis para o usuário.

A seguir, um exemplo de código ruim contendo credenciais de conta codificadas:

  IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
string userName = "joe", domainName = "acmecorp", password="p@Ssw0rd";
const int LOGON32_PROVIDER_DEFAULT = 0;
// This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
const int SecurityImpersonation = 2;
tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(userName, 
                             domainName, 
                             password, 
                             LOGON32_LOGON_INTERACTIVE, 
                             LOGON32_PROVIDER_DEFAULT,
                             ref tokenHandle);

Como o código criptografa segredos?

Verifique se o código usa DPAPI para criptografar strings e credenciais de conexão. Não armazene segredos na Autoridade local de Segurança (LSA) porque a conta usada para acessar o LSA requer privilégios estendidos. Para obter informações sobre o uso do DPAPI, consulte “Como: Criar uma Biblioteca DPAPI” na seção “Como Fazer” dos padrões e práticas da Microsoft Volume I, Construindo aplicativos ASP.NET seguros: autenticação, autorização e comunicação segura ou como: criptografar seções de configuração em ASP.NET 2.0 Usando DPAPI.

O código guarda segredos no registro?

Se o código armazena segredos em HKEY_LOCAL_MACHINE, verifique se os segredos são primeiro criptografados e, em seguida, protegidos com uma LCA restrita. Uma ACL não é necessária se o código armazena segredos em HKEY_CURRENT_USER porque essa chave de registro está automaticamente restrita a processos executados sob a conta de usuário associada.

O código elimina segredos da memória?

Procure por falhas para limpar segredos da memória após o uso. Como o tempo de execução da língua comum (CLR) gerencia a memória para você, isso é realmente mais difícil de fazer no código gerenciado do que no código nativo. Para garantir que os segredos sejam adequadamente esclarecidos, verifique se as seguintes medidas foram tomadas:

  • As strings não devem ser usadas para armazenar segredos; eles não podem ser alterados ou efetivamente limpas. Em vez disso, o código deve usar uma matriz byte ou um CLR 2.0 SecureString.
  • Qualquer que seja o tipo que o código usa, ele deve chamar o método Clear assim que terminar com os dados.
  • Se um segredo é bimeto em disco, ele pode persistir por longos períodos de tempo e pode ser difícil de limpar completamente. Certifique-se de que o GCHandle.Alloc e o GCHandleType.Pinned sejam usados para evitar que os objetos gerenciados sejam bimetos em disco.

Os dados confidenciais são armazenados em locais previsíveis?

Dados confidenciais devem ser armazenados e transmitidos de forma criptografada; nada menos convida roubo. Por exemplo, um erro comum é armazenar senhas do servidor de banco de dados no arquivo ASP.NET Web.config, como mostrado no exemplo a seguir.

  <!-- web.config -->
<connectionStrings>
  <add name="MySQLServer" 
       connectionString="Initial Catalog=finance;data 
       source=localhost;username='Bob' password='pwd';"
       providerName="System.Data.SqlClient"/>
 </connectionStrings>

Em vez disso, as strings de conexão devem ser criptografadas com o utilitário Aspnet_regiis. A sintaxe de comando é:

aspnet_regiis -pe “connectionStrings” -app “/MachineDPAPI” -prov “DataProtectionConfigurationProvider”

O arquivo Web.config após a criptografia deve ser semelhante ao seguinte.

  <!web.config after encrypting the connection strings section -->
<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
  <EncryptedData>
    <CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAexuIJ/8oFE+sGTs7jBKZdgQAAAACAAAAAAADZgAAqAAAABAAAAAKms84dyaCPAeaSC1dIMIBAAAAAASAAACgAAAAEAAAAKaVI6aAOFdqhdc6w1Er3HMwAAAAcZ00MZOz1dI7kYRvkMIn/
BmfrvoHNUwz6H9rcxJ6Ow41E3hwHLbh79IUWiiNp0VqFAAAAF2sXCdb3fcKkgnagkHkILqteTXh</CipherValue>
    </CipherData>
  </EncryptedData>
</connectionStrings>
...

Da mesma forma, o código não deve armazenar credenciais de autenticação de formulários no arquivo Web.config, conforme ilustrado no exemplo a seguir.

  <authentication mode="Forms">
   <forms name="App" loginUrl="/login.aspx">
      <credentials passwordFormat = "Clear" 
         <user name="UserName1" password="Password1"/>
         <user name="UserName2" password="Password2"/>
         <user name="UserName3" password="Password3"/>
      </credentials>
   </forms>
</authentication>

Em vez disso, use uma loja externa com acesso bem controlado, como o Active Directory ou um banco de dados SQL Server.

Criptografia

Revise o código para ver se ele usa criptografia para fornecer privacidade, não-repúdio, adulteração ou autenticação. A Tabela 9 mostra as vulnerabilidades que podem ser introduzidas se a criptografia for usada de forma inadequada.

Tabela 9: Vulnerabilidades e Implicações da Criptografia

Vulnerabilidade Implicações
Usando criptografia personalizada Isso é menos seguro do que a criptografia testada e testada fornecida pela plataforma.
Usando o algoritmo errado ou muito pequeno um tamanho de chave Algoritmos mais novos aumentam a segurança. Tamanhos de teclas maiores aumentam a segurança.
Falhando em proteger chaves de criptografia Os dados criptografados são tão seguros quanto a chave de criptografia.
Usando a mesma chave por um período prolongado de tempo Uma chave estática é mais provável de ser descoberta com o tempo.

As seguintes perguntas ajudam a identificar áreas vulneráveis:

  • O código usa algoritmos criptográficos personalizados?
  • O código usa o algoritmo correto e um tamanho de chave adequado?
  • Como o código gerencia e armazena chaves de criptografia?
  • O código gera números aleatórios para fins criptográficos?

O código usa algoritmos criptográficos personalizados?

Procure rotinas criptográficas personalizadas. Certifique-se de que o código usa o namespace System.Cryptography. A criptografia é notoriamente difícil de implementar corretamente. As APIs do Windows Crypto são a implementação de algoritmos derivados de anos de pesquisa e estudo acadêmico. Alguns pensam que um algoritmo menos conhecido é mais seguro, mas isso não é verdade. Algoritmos criptográficos são matematicamente comprovados, e aqueles que receberam mais revisão são geralmente mais eficazes. Um algoritmo obscuro e não testado não protege sua implementação falha de um determinado invasor.

O código usa o algoritmo correto e um tamanho de chave adequado?

Revise seu código para ver quais algoritmos e tamanhos-chave ele usa. Reveja as seguintes perguntas:

  • O código usa criptografia simétrica?
    • Se assim for, verifique se ele usa Rijndael (agora chamado de Advanced Encryption Standard [AES]) ou Triple Data Encryption Standard (3DES) quando os dados criptografados precisam ser persistidos por longos períodos de tempo. Use os algoritmos RC2 e DES mais fracos (mas mais rápidos) apenas para criptografar dados que têm uma vida útil curta, como dados de sessão.
  • O código usa os maiores tamanhos de chave possíveis?
    • Use o maior tamanho de chave possível para o algoritmo que você está usando. Tamanhos de teclas maiores tornam os ataques contra a chave muito mais difíceis, mas podem degradar o desempenho.

Como o código gerencia e armazena chaves de criptografia?

Procure por má gestão de chaves. Sinalizar valores-chave codificados: deixá-los no código ajudará a garantir que a criptografia seja quebrada. Certifique-se de que os valores-chave não sejam passados do método para o método por valor, pois isso deixará muitas cópias do segredo na memória a serem descobertas por um invasor.

O código gera números aleatórios para fins criptográficos?

Procure por geradores de números aleatórios pobres. Você deve ter certeza de que o código usa System.Security.Cryptography.RNGCryptoServiceProvider para gerar números aleatórios criptograficamente seguros. A classe Random não gera números verdadeiramente aleatórios que não são repetíveis ou previsíveis.

Código inseguro

Preste atenção especial a qualquer código compilado com o switch /inseguro. Este código não tem todo o código gerenciado normal de proteção é dado. Procure possíveis estouros de buffer, matriz fora de erros vinculados, subfluxo e estouro de integer, bem como erros de truncação de dados. A Tabela 10 mostra possíveis vulnerabilidades que podem ser introduzidas em código inseguro.

Tabela 10: Vulnerabilidades e Implicações do Código Inseguro

Vulnerabilidade Implicações
Buffer invadido em código ou código não gerenciado marcado /inseguro Permite a execução arbitrária de código usando os privilégios do aplicativo em execução.
Estouro inteiro em código ou código não gerenciado marcado /inseguro Cálculos inesperados resultam em instabilidade do sistema ou permitem que um invasor leia memória arbitrária.
Problema de sequência de formato em código ou código não gerenciado marcado /inseguro Um invasor pode ler ou modificar memória arbitrária.
Matriz fora dos limites em código ou código não gerenciado marcado /inseguro A falha na verificação dos limites de matriz antes do acesso pode permitir que um invasor leia memória arbitrária.
Truncação de dados em código ou código não gerenciado marcado /inseguro Truncação de dados inesperada pode resultar em instabilidade no sistema ou permitir que um invasor leia memória arbitrária.

Revise o código inseguro usando as seguintes perguntas:

  • O código é suscetível a excessos de buffer?
  • O código é suscetível a transbordamentos inteiros?
  • O código é suscetível a problemas de sequência de caracteres?
  • O código é suscetível a 7 erros vinculados?

O código é suscetível a excessos de buffer?

Os excessos de buffer são uma vulnerabilidade que pode levar à execução de código arbitrário. Ao rastrear através de códigos não gerenciados ou inseguros, certifique-se de que as seguintes regras sejam seguidas:

  • Certifique-se de que todas as funções copiam dados de comprimento variável em um buffer e usem um parâmetro de comprimento máximo corretamente.
  • Certifique-se de que o código não depende de outra camada ou nível para truncação de dados.
  • Se você vir um problema, certifique-se de que o código trunca os dados em vez de expandir o buffer para encaixá-los. A expansão do buffer pode apenas mover o problema rio abaixo.
  • Certifique-se de que qualquer código não gerenciado foi compilado com a opção /GS.

O aplicativo não deve conter código semelhante ao exemplo a seguir.

public void ProcessInput()
{
  char[] data = new char[255];  
  GetData(data);
}

public unsafe void GetData(char[] buffer)
{
 int ch = 0;
 fixed (char* pBuf = buffer)
 {
   do
   {
     ch = System.Console.Read();
     *(pBuf++) = (char)ch;
   } while(ch != '\n');
 }
}

Neste exemplo de código, um estouro ocorre sempre que uma única linha tem mais de 255 caracteres. Há dois problemas neste código:

  • A função ProcessInput aloca apenas espaço suficiente para 255 caracteres.
  • A função GetData não verifica o tamanho da matriz enquanto a preenche.

O código é suscetível a transbordamentos inteiros?

Esse problema ocorre se um cálculo faz com que um valor de dados seja maior ou menor do que seu tipo de dados permite. Isso fará com que o valor se enrole e se torne muito maior ou menor do que o esperado. Ao revisar os dados através de códigos não gerenciados ou inseguros, certifique-se de que qualquer local onde um usuário possa dar entrada que resulte em um cálculo não cause uma condição de subfluxo ou estouro.

O aplicativo não deve conter código semelhante ao exemplo a seguir.

int[] filter(uint len, int[] numbers)
{
  uint newLen =  len * 3/4;
  int[] buf = new int[newLen];
  int j = 0;
  for(int i = 0; i < len; i++)
  {
    if (i % 4 != 0)
    buf[j++] = numbers[i];
  }
  return buf;
}

O problema neste exemplo é que, no cálculo do valor para len, o código primeiro computa len * 3 e depois divide por 4. Quando len é grande o suficiente (cerca de 1,4 bilhão), len * 3 transborda e newLen é atribuído um valor que é muito pequeno. O resultado está fora do alcance do acesso à matriz na matriz buf.

O código é suscetível a problemas de sequência de caracteres?

Problemas de sequência de caracteres de formato são causados pela forma como as funções printf lidam com variáveis e pela diretiva de formato %n. Embora você revise códigos não gerenciados ou inseguros, certifique-se de que os dados de sequência de formato nunca contenham entrada do usuário.

O aplicativo não deve conter código semelhante ao exemplo a seguir.

void main (int argc,
           char **argv)
{
  /* Whatever the user said, spit back! */
  printf (argv[1]);
}

Neste exemplo, a entrada não confiável na forma de um parâmetro de linha de comando é passada diretamente para uma instrução printf. Isso significa que um invasor pode incluir diretivas de sequência de string % na sequência e forçar o aplicativo a retornar ou modificar memória arbitrária na pilha.

O código é suscetível a <> erros vinculados?

Erros de indexação de array, como excessos de buffer, podem levar à superescrita da memória em locais arbitrários. Isso, por sua vez, pode levar à instabilidade da aplicação, ou, com um ataque cuidadosamente construído, injeção de código. Enquanto você revisa códigos não gerenciados ou inseguros, certifique-se de que as seguintes regras sejam seguidas:

  • Com o código C/C++, certifique-se de que os índices vão de zero a n-1, onde n é o número de elementos de matriz.
  • Sempre que possível, certifique-se de que o código não use parâmetros de entrada como índices de matriz.
  • Certifique-se de que quaisquer parâmetros de entrada usados como índices de matriz sejam validados e limitados para garantir que os limites máximos e mínimos de matriz não possam ser excedidos.

APIs não gerenciados potencialmente perigosas

Além das verificações realizadas para código inseguro, você deve revisar o código não gerenciado para o uso de APIs potencialmente perigosas, como strcpy e strcat. (Uma lista completa de APIs potencialmente perigosas está listada na Tabela 12.) Certifique-se de revisar quaisquer chamadas interop, bem como o próprio código não gerenciado, para garantir que suposições ruins não sejam feitas à medida que o controle de execução passa de código gerenciado para código não gerenciado. A Tabela 11 mostra potenciais vulnerabilidades que podem surgir em código não gerenciado.

Tabela 11: Vulnerabilidades e Implicações da API não gerenciados

Vulnerabilidade Implicações
Uma API não gerenciado potencialmente perigosa é chamada de indevidamente Um invasor poderia explorar a fraqueza na API potencialmente perigosa para obter acesso a locais de memória arbitrária ou executar código arbitrário.

O código chama APIs potencialmente perigosos não gerenciados?

Funções não gerenciados potencialmente perigosas podem ser categorizadas da seguinte forma:

  • Natural de Funções (UF). Essas funções não esperam um parâmetro vinculado explícito para o número de bytes que podem ser modificados para um de seus parâmetros. Estas são tipicamente as funções mais potencialmente perigosas e nunca devem ser usadas.
  • NULL Exterminados (NTF). Essas funções requerem uma sequência final de NULL. Se eles forem fornecidos uma sequência sem término NULL, eles podem substituir a memória. Se o código usar funções rescindidas null, certifique-se de que o loop não tenha um espaço reservado adicional para NULL; por exemplo, para(i = 0; i <= 512; i++) deve ser < 512 não <= 512.
  • Funções não-NULAs (NNTF). A saída da maioria das funções de string é nula; no entanto, a saída de alguns não é. Estes requerem tratamento especial para evitar defeitos de programação. Se o código usar funções não-NULL, certifique-se de que o loop tenha um espaço reservado adicional para NULL.
  • Funções de formato (FF). As funções de string de formato permitem que um programador formatar sua entrada e saída. Se o formato não for fornecido, os dados podem ser manipulados e podem levar a defeitos de programação.

A Tabela 12 mostra uma gama de APIs não gerenciados potencialmente perigosas e as categorias associadas nas quais elas se enquadram.

Tabela 12: APIs não gerenciados potencialmente perigosos

Funções Categoria
Strcpy UF
Strcat UF
Strcat Ntf
Strlen Ntf
Strncpy NNTF
Strncat Ntf
Strcmp Ntf
Strcmp Ntf
Mbcstows NNTF
_strdup Ntf
_strrev Ntf
Strstr Ntf
Strstr Ntf
Sprintf FF
_snprintf FF
Printf FF
Fprintf FF
Obtém UF
Scanf FF
Fscanf FF
Sscanf FF, NTF,
Strcspn Ntf
MultiByteToWideChar NNTF
WideCharToMultiByte NNTF
GetShortPathNameW Ntf
GetLongPathNameW Ntf
WinExec Ntf
CriarprocessW Ntf
GetEnvironmentVariableW Ntf
SetEnvironmentVariableW Ntf
SetEnvironmentVariableW Ntf
ExpandirEnvironmentStringsW Ntf
SearchPathW Ntf
SearchPathW Ntf
SearchPathW Ntf
Lstrcpy UF
Wcscpy UF
_mbscpy UF
StrCpyA UF
StrCpyW UF
LstrcatA UF
LstrcatW UF
Wcscat UF
_mbscat UF
Wcslen Ntf
_mbslen Ntf
_mbstrlen Ntf
lstrlenA NTF
lstrlenW NTF
Wcsncpy NNTF
_mbsncpy NNTF
StrCpyN NNTF
lstrcpynW NTF
lstrcatnA NTF
lstrcatnW NTF
Wcsncat NTF
_mbsncat NTF
_mbsnbcat NTF
lstrcmpA NTF
lstrcmpW NTF
StrCmp NTF
Wcscmp NTF
_mbscmp NTF
Strcoll NTF
Wcscoll NTF
_mbscoll NTF
_stricmp NTF
lstrcmpiA NTF
lstrcmpiW NTF
_wcsicmp NTF
_mbsicmp NTF
StrCmp NTF
_stricoll NTF
_wcsicoll NTF
_mbsicoll NTF
StrColl NTF
_wcsdup NTF
_mbsdup NTF
StrDup NTF
_wcsrev NTF
_mbsrev NTF
_strlwr NTF
_mbslwr NTF
_wcslwr NTF
_strupr NTF
_mbsupr NTF
_wcsupr NTF
Wcsstr NTF
_mbsstr NTF
Strspn NTF
Wcsspn NTF
_mbsspn NTF
Strpbrk NTF
Wcspbrk NTF
_mbspbrk NTF
Wcsxfrm NTF
Wcscspn NTF
_mbcscpn NTF
Swprintf FF
wsprintfA FF
wsprintfW FF
Vsprintf FF
Vswprintf FF
_snwprintf FF
_vsnprintf FF
_vsnwprintf FF
Vprintf FF
Vwprintf FF
Vfprintf FF
Vwfprintf FF
_getws UF
Fwscanf Ff
Wscanf Ff
Swscanf Ff
OemToChara UF
OemToCharW UF
CharTooema UF
CharTooemw UF
Charuppera Ntf
CharupperW Ntf
CharupperBuffw Ntf
CharLowera Ntf
CharLowerW Ntf
CharLowerBuffW Ntf

Auditoria e Registro

O código usa auditoria e registro de forma eficaz? A Tabela 12 mostra as vulnerabilidades que podem ser introduzidas se a auditoria e o registro não forem usados ou se forem usados incorretamente.

Tabela 13: Auditoria e Registro de Vulnerabilidades e Implicações

Vulnerabilidade Implicações
Falta de registro É difícil detectar e repelir tentativas de intrusão.
Dados confidenciais revelados em logs Um invasor pode usar credenciais registradas para atacar o servidor ou pode roubar outros dados confidenciais do registro.

O aplicativo registra dados confidenciais?

Revise o código para ver se os detalhes confidenciais estão registrados. Credenciais e dados confidenciais do usuário não devem ser registrados. Os aplicativos podem funcionar com informações que exigem privilégios maiores para visualizar do que o arquivo de registro. Expor dados confidenciais em um arquivo de registro torna mais provável que os dados sejam roubados.

Multi-Threading

O código de várias roscas é propenso a problemas sutis relacionados ao tempo ou condições de corrida que podem resultar em vulnerabilidades de segurança. Para localizar códigos de várias rosca, pesquise o código-fonte do texto “Thread” para identificar onde novos objetos thread são criados, como mostrado no exemplo de código a seguir.

Thread t = new Thread(new ThreadStart(someObject.SomeThreadStartMethod));

A Tabela 14 mostra potenciais vulnerabilidades que podem surgir em códigos multi-threaded.

Tabela 14: Enroscando vulnerabilidades e implicações

Vulnerabilidade Implicações
Condições de corrida Falha lógica e aplicação incorretas
Problemas de sincronização Mau funcionamento do aplicativo

As seguintes perguntas de revisão ajudam a identificar potenciais vulnerabilidades de threading:

  • O código está sujeito a condições raciais?
  • O código se passa?
  • O código contém construtores de classe estática?
  • O código sincroniza métodos Descarte?

O código está sujeito a condições raciais?

Verifique as condições da corrida, especialmente em métodos estáticos e construtores. Considere o seguinte exemplo de código.

private static int amtRecvd = 0;
public static int IncrementAmountReceived(int increment)
{
  return(amtRecvd += increment);
}

Se dois segmentos chamarem esse código ao mesmo tempo, pode resultar em um cálculo incorreto para o valor amtRecvd.

O código é particularmente vulnerável às condições de corrida se ele armazena os resultados de uma verificação de segurança — por exemplo, em uma variável estática ou global — e, em seguida, usa o sinalizador para tomar decisões de segurança subsequentes.

O código se passa?

O segmento que cria um novo segmento está se passando atualmente? O novo segmento sempre assume o contexto de segurança em nível de processo e não o contexto de segurança do segmento existente.

O código contém construtores de classe estática?

Revise os construtores de classe estática para verificar se eles não estão vulneráveis se dois ou mais segmentos os acessam simultaneamente. Se necessário, sincronize os fios para evitar essa condição.

O código sincroniza métodos Descarte?

Se o método ‘Descartar’ de um objeto não estiver sincronizado, é possível que dois segmentos executem Descarte no mesmo objeto. Isso pode causar problemas de segurança, especialmente se o código de limpeza liberar manipuladores de recursos não gerenciados, como arquivos, processos ou alças de segmento.

Matriz de vulnerabilidade/pergunta

A Tabela 15 associa vulnerabilidades a perguntas. Use esta tabela para desenvolver um conjunto de perguntas a serem feitas durante a revisão de código se você estiver preocupado com uma vulnerabilidade específica.

Tabela 15: Vulnerabilidade/Matriz de Perguntas

Vulnerabilidade Perguntas
Injeção SQL  
Entrada não validada usada para gerar consultas SQL A aplicação é suscetível à injeção sql?
  O código usa procedimentos armazenados parametrizados?
  O código usa parâmetros em declarações SQL?
  O código tenta filtrar a entrada?
Scripting entre sites  
Entrada não validada e não confiável no fluxo de saída HTML O código ecoa a entrada do usuário ou os parâmetros de URL de volta a uma página da Web?
  O código persiste na entrada do usuário ou nos parâmetros de URL em um armazenamento de dados que mais tarde poderia ser exibido em uma página da Web?
Validação de Entradas / Dados  
Dependência da validação do lado do cliente O código depende da validação do lado do cliente?
Uso de nomes de arquivos de entrada, URLs ou nomes de usuários para decisões de segurança O código é suscetível a ataques de canonização?
Filtros somente aplicativos para entrada maliciosa O código valida dados de todas as fontes?
  O código centraliza sua abordagem?
Segurança de acesso ao código  
Uso inadequado de demandas de link ou afirma O código usa demandas de link ou afirma chamadas?
Código permite chamadas não confiáveis Seu código usa o AllowPartiallyTrustedCallers Attribute?
  O código usa permissões potencialmente perigosas?
  O código dá muita confiança às dependências?
Gerenciamento de exceções  
Falha ao usar o manuseio estruturado de exceção O código usa verificação de erro adequada e consistente?
  O aplicativo falha com segurança em caso de exceções?
Revelando muita informação para o cliente As mensagens de erro dão muita informação?
Representação  
Revelando credenciais de conta de serviço para o cliente O aplicativo usa credenciais de representação codificadas e codificadas?
Código é executado com privilégios maiores do que o esperado O código limpa corretamente quando usa personificação?
Dados confidenciais  
Armazenando segredos em código Os segredos da loja de códigos?
Armazenamento de segredos em texto claro Os dados confidenciais são armazenados em locais previsíveis?
Passando dados confidenciais em texto claro sobre redes Os segredos da loja de códigos?
Criptografia  
Usando criptografia personalizada A equipe desenvolveu algoritmos criptográficos?
Usando o algoritmo errado ou muito pequeno um tamanho de chave O código usa o algoritmo certo com um tamanho de chave adequado?
  O código gera números aleatórios para fins criptográficos?
Falhando em proteger chaves de criptografia Como o código gerencia e armazena chaves de criptografia?
Usando a mesma chave por um período prolongado de tempo Como o código gerencia e armazena chaves de criptografia?
Código inseguro  
Buffer invadido em código ou código não gerenciado marcado /inseguro O código é suscetível a excessos de buffer?
Estouro inteiro em código ou código não gerenciado marcado /inseguro O código é suscetível a transbordamentos inteiros?
Problema de sequência de formato em código ou código não gerenciado marcado /inseguro O código é suscetível a problemas de sequência de caracteres?
Matriz fora dos limites em código ou código não gerenciado marcado /inseguro O código é suscetível a erros fora do limite?
Truncação de dados em código ou código não gerenciado marcado /inseguro  
APIs não gerenciados potencialmente perigosas  
Uma API não gerenciado potencialmente perigosa é chamada de indevidamente O código chama APIs potencialmente perigosos não gerenciados?
Auditoria e Registro  
Dados confidenciais revelados em logs O código registra dados confidenciais?
Multi-Threading  
Condições de corrida O código está sujeito a condições raciais?
Problemas de sincronização O código contém construtores de classe estática?
  O código sincroniza métodos Descarte?

Recursos adicionais

Realimentação

Forneça feedback usando um Wiki ou um e-mail:

Estamos particularmente interessados em feedback sobre o seguinte:

  • Questões técnicas específicas para recomendações
  • Problemas de utilidade e usabilidade

Suporte técnico

O suporte técnico para os produtos e tecnologias da Microsoft mencionados nesta orientação é fornecido pelos Serviços de Suporte da Microsoft. Para obter informações sobre suporte ao produto, visite o site de suporte da Microsoft em https://support.microsoft.com.

Grupos comunitários e de notícias

O apoio da comunidade é fornecido nos fóruns e grupos de notícias:

  • MSDN Newsgroups:https://www.microsoft.com/communities/newsgroups/default.mspx
  • ASP.NET Fóruns: http://forums.asp.net

Para obter o maior benefício, encontre o grupo de notícias que corresponde à sua tecnologia ou problema. Por exemplo, se você tiver um problema com ASP.NET recursos de segurança, você usaria o fórum ASP.NET Security.

Colaboradores e Revisores

  • Colaboradores externos e revisores: Akshay Aggarwal; Anil John; Frank Heidt; Jason Schmitt, SPI Dynamics; Keith Brown, Pluralsight; Loren Kornfelder
  • Grupo de Produtos da Microsoft: Don Willits, Eric Jarvi, Randy Miller, Stefan Schackow
  • Colaboradores e Revisores de TI da Microsoft: Shawn Veney
  • Microsoft EEG: Eric Brechner
  • Padrões e práticas da Microsoft Colaboradores e Revisores: Carlos Farre
  • Equipe de teste: Larry Brader, Microsoft Corporation; Nadupalli Venkata Surya Sateesh, Sivanthapatham Shanmugasundaram, Infosys Technologies Ltd.
  • Equipe de edição: Nelly Delgado, Microsoft Corporation
  • Gerenciamento de lançamentos: Sanjeev Garg, Microsoft Corporation

Artigo Orignal