Michael Howard e Keith Brown
Este artigo pressupõe que você esteja familiarizado com C++, C# e SQL
Resumo: Quando se trata de questões de segurança, há muitas situações que podem causar problemas. Você provavelmente confia em todos os códigos executados em sua rede, dá a todos os usuários acesso a arquivos importantes e nunca se preocupa em verificar se o código em suas máquinas foi alterado. Você também pode não ter um software antivírus instalado, não conseguir proteger seu próprio código e conceder permissões demais a muitas contas. Você pode até ser descuidado ao usar uma série de funções integradas que permitem invasões maliciosas e pode deixar as portas do servidor abertas sem qualquer monitoramento. Obviamente, podemos dar muitos mais exemplos. Quais são as questões realmente importantes (ou seja, os erros mais perigosos que devem receber atenção imediata para evitar comprometer seus dados e sistemas)? Os especialistas em segurança Michael Howard e Keith Brown oferecem dez dicas para ajudá-lo.
-------------------------------------------------- ----------------------------------
As questões de segurança envolvem muitos aspectos. Os riscos de segurança podem vir de qualquer lugar. Você pode ter escrito um código de tratamento de erros ineficaz ou ter sido muito generoso ao conceder permissões. Você pode ter esquecido quais serviços estão sendo executados no seu servidor. Você pode aceitar todas as entradas do usuário. E assim por diante. Para lhe dar uma vantagem inicial na proteção do seu computador, rede e código, aqui estão dez dicas que você pode seguir para uma estratégia de rede mais segura.
1. Confiar na entrada do usuário coloca você em risco.
Mesmo que você não leia o resto, lembre-se disto: “Não confie na entrada do usuário”. O problema surge se você sempre presumir que os dados são válidos e não maliciosos. A maioria das vulnerabilidades de segurança envolve invasores alimentando dados gravados maliciosamente em servidores.
Confiar na exatidão da entrada pode levar a buffer overflows, ataques de script entre sites, ataques de código de inserção SQL e muito mais.
Vamos discutir detalhadamente esses possíveis vetores de ataque.
2. Evitar estouro de buffer
Quando um invasor fornece comprimento de dados maior do que o aplicativo espera, ocorre um estouro de buffer e os dados transbordam para o espaço de memória interna. O estouro de buffer é principalmente um problema de C/C++. Eles são uma ameaça, mas geralmente fáceis de consertar. Vimos apenas dois buffer overflows que não eram óbvios e difíceis de corrigir. O desenvolvedor não previu que os dados fornecidos externamente seriam maiores que o buffer interno. O estouro causa a corrupção de outras estruturas de dados na memória, que muitas vezes são exploradas por invasores para executar códigos maliciosos. Erros de índice de matriz também podem causar estouros e estouros de buffer, mas isso é menos comum.
Dê uma olhada no seguinte trecho de código C++:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
Qual é o problema? Na verdade, não há nada de errado com este código se cBuffSrc e cbBuffSrc vierem de uma fonte confiável (como um código que não confia nos dados e, portanto, verifica sua validade e tamanho). No entanto, se os dados vierem de uma fonte não confiável e não tiverem sido verificados, um invasor (fonte não confiável) poderá facilmente tornar cBuffSrc maior que cBuffDest e também definir cbBuffSrc como maior que cBuffDest. Quando memcpy copia os dados para cBuffDest, o endereço de retorno de DoSomething é alterado e, como cBuffDest é adjacente ao endereço de retorno no quadro de pilha da função, o invasor pode realizar algumas operações maliciosas por meio do código.
A maneira de compensar é não confiar na entrada do usuário e não confiar em nenhum dado transportado em cBuffSrc e cbBuffSrc:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
const DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}
Esta função demonstra três propriedades de uma função escrita corretamente que pode reduzir estouros de buffer. Primeiro, exige que o chamador forneça o comprimento do buffer. Claro, você não pode confiar cegamente nesse valor! Em seguida, em uma compilação de depuração, o código detecta se o buffer é realmente grande o suficiente para conter o buffer de origem. Caso contrário, uma violação de acesso poderá ser acionada e o código poderá ser carregado no depurador. Ao depurar, você ficará surpreso com quantos bugs encontrará. Finalmente e mais importante, as chamadas para memcpy são defensivas porque não copiam mais dados do que o buffer de destino pode conter.
Como parte do impulso de segurança do Windows® na Microsoft, criamos uma lista de funções seguras de manipulação de strings para programadores C. Você pode encontrá-los em Strsafe.h: Safer String Handling in C (Inglês).
3. Impedir scripts entre sites
Os ataques de scripts entre sites são um problema exclusivo da Web. Eles podem danificar os dados do cliente por meio de uma vulnerabilidade oculta em uma única página da Web. Imagine as consequências do seguinte trecho de código ASP.NET:
<script language=c#>
Response.Write("Olá," + Request.QueryString("nome"));
</script>
Quantas pessoas viram código semelhante? Mas surpreendentemente tem problemas! Normalmente, os usuários acessarão esse código usando uma URL semelhante a esta:
http://explorationair.com/welcome.aspx?name=Michael
O código C# assume que os dados são sempre válidos e contêm apenas um nome. No entanto, um invasor pode abusar desse código fornecendo script e código HTML como nomes. Se você inserir o seguinte URL
http://northwindtraders.com/welcome.aspx?name=<script>alert(' Olá!');
</script>
Você verá uma página web com uma caixa de diálogo dizendo "Olá!" Você pode estar dizendo: "E daí?" Imagine que um invasor pudesse enganar um usuário para que ele clicasse em um link como este, mas a string de consulta contivesse algum script e HTML realmente perigoso, obtendo assim o cookie do usuário e enviando-o para um site de propriedade de o invasor; agora o invasor tem acesso às suas informações privadas de cookies, ou pior.
Para evitar isso, existem duas maneiras. A primeira é desconfiar da entrada e limitar estritamente o que o nome de usuário contém. Por exemplo, você pode usar expressões regulares para verificar se o nome contém apenas um subconjunto comum de caracteres e não é muito grande.
de
código C# mostra como realizar esta etapa:
Regex r = new Regex(@"^[w]{1,40}$");
// bom! A corda está ok
} outro {
// não é bom! String inválida
}
Este código usa expressões regulares para verificar se uma string contém apenas de 1 a 40 letras ou números. Esta é a única maneira segura de determinar se um valor está correto.
Não há como HTML ou script enganar essa expressão regular! Não use expressões regulares para procurar caracteres inválidos e rejeite solicitações se esses caracteres inválidos forem encontrados, pois é fácil perder alguma coisa.
A segunda precaução é codificar em HTML todas as entradas como saída. Isso reduz tags HTML perigosas a caracteres de escape mais seguros. Você pode usar HttpServerUtility.HtmlEncode em ASP.NET ou Server.HTMLEncode em ASP para escapar de quaisquer strings potencialmente problemáticas.
4. Não solicite permissões sa
O último ataque de confiança de entrada que discutiremos é a inserção de código SQL. Muitos desenvolvedores escrevem código que recebe entrada e usa essa entrada para criar consultas SQL que se comunicam com um armazenamento de dados de back-end, como Microsoft® SQL Server™ ou Oracle.
Dê uma olhada no seguinte trecho de código:
void DoQuery(string Id) {
SqlConnection sql=new SqlConnection(@"fonte de dados=localhost;" +
"id do usuário=sa;senha=senha;");
sql.Open();
sqlstring= "SELECT foi enviado" +
" DO envio WHERE id='" + Id + "'";
SqlCommand cmd = new SqlCommand(sqlstring,sql);
•••
Este código tem três falhas graves. Primeiro, ele estabelece uma conexão do serviço Web com o SQL Server como a conta do administrador do sistema sa. Em breve você verá as armadilhas disso. Segundo ponto, preste atenção à prática inteligente de usar "senha" como senha da conta sa!
Mas a verdadeira preocupação é a concatenação de strings que constrói instruções SQL. Se o usuário inserir 1001 para ID, você obterá a seguinte instrução SQL, que é totalmente válida.
SELECT hasshipped FROM Shipping WHERE id = '1001'
Mas os invasores são muito mais criativos do que isso. Eles inseririam um "'1001' DROP table shipping --" para o ID, que executaria uma consulta como esta:
SELECT foi enviado DE
envio ONDE id = '1001'
Envio da tabela DROP -- ';
Muda a forma como a consulta funciona. Este código não apenas tenta determinar se algo foi enviado, mas também descarta (exclui) a tabela de remessa! Operador – é o operador de comentário em SQL, que torna mais fácil para os invasores construírem uma série de instruções SQL válidas, mas perigosas!
Neste momento, você deve estar se perguntando como qualquer usuário pode excluir a tabela do banco de dados SQL Server. Claro que você está certo, apenas administradores podem fazer esse trabalho. Mas aqui você está se conectando ao banco de dados como sa, e sa pode fazer o que quiser no banco de dados SQL Server. Nunca conecte-se ao SQL Server como sa a partir de qualquer aplicativo; a abordagem correta é usar a Autenticação Integrada do Windows, se apropriado, ou conectar-se como uma conta predefinida com permissões apropriadas.
Corrigir problemas de código de inserção SQL é fácil. Usando procedimentos e parâmetros armazenados SQL, o código a seguir mostra como criar tal consulta - e como usar expressões regulares para confirmar se a entrada é válida, já que nossa transação especifica que o ID da remessa pode ter apenas de 4 a 10 dígitos:
Regex r = new Regex(@"^d{4,10}$");
if (!r.Match(Id).Sucesso)
lançar new Exception("ID inválido")
;
string str="sp_HasShipped";
SqlCommand cmd = new SqlCommand(str,sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
Estouros de buffer, scripts entre sites e ataques de código de inserção SQL são exemplos de problemas de entrada confiável. Todos esses ataques são atenuados por um mecanismo que considera todas as informações prejudiciais, salvo prova em contrário.
5. Preste atenção ao código de criptografia!
Vamos dar uma olhada em algo que pode nos surpreender. Descobri que mais de trinta por cento do código de segurança que examinamos apresentava vulnerabilidades de segurança. Talvez a vulnerabilidade mais comum seja o seu próprio código de criptografia, que provavelmente será vulnerável. Nunca crie seu próprio código de criptografia, é uma tarefa tola. Não pense que só porque você tem seu próprio algoritmo de criptografia, outros não poderão quebrá-lo. Os invasores têm acesso a depuradores e também têm tempo e conhecimento para determinar como os sistemas funcionam – muitas vezes quebrando-os em questão de horas. Você deve usar o Win32® CryptoAPI, o namespace System.Security.Cryptography fornece vários algoritmos de criptografia excelentes e testados.
6. Reduza a possibilidade de ser atacado.
Se mais de 90% dos usuários não solicitarem, um recurso não deverá ser instalado por padrão. O Internet Information Services (IIS) 6.0 segue esta recomendação de instalação, sobre a qual você pode ler no artigo de Wayne Berry "Inovações nos serviços de informações da Internet permitem proteger firmemente dados e processos de servidor seguros", lançado este mês. A ideia por trás dessa estratégia de instalação é que você não preste atenção aos serviços que não está usando e, se esses serviços estiverem em execução, poderão ser explorados por outros. Se um recurso estiver instalado por padrão, ele deverá ser executado sob o princípio de autorização mínima. Ou seja, não permita que aplicativos sejam executados com privilégios de administrador, a menos que seja necessário. É melhor seguir este conselho.
7. Use o princípio da autorização mínima
Os sistemas operacionais e os tempos de execução de linguagem comum têm uma política de segurança por vários motivos. Muitas pessoas presumem que a principal razão pela qual esta política de segurança existe é evitar que os usuários causem danos intencionalmente: acessando arquivos que não estão autorizados a acessar, reconfigurando a rede para atender às suas necessidades e outros comportamentos flagrantes. Sim, esse tipo de ataque interno é comum e precisa ser evitado, mas há outro motivo para seguir essa estratégia de segurança. Ou seja, construir barreiras defensivas em torno do código para evitar que os usuários causem estragos na rede por meio de ações intencionais ou (como acontece frequentemente) não intencionais. Por exemplo, um anexo baixado via e-mail, quando executado na máquina de Alice, fica restrito aos recursos que Alice pode acessar. Se um anexo contiver um cavalo de Troia, uma boa estratégia de segurança é limitar os danos que ele pode causar.
Ao projetar, criar e implementar aplicativos de servidor, você não pode presumir que todas as solicitações vêm de usuários legítimos. Se um bandido lhe enviar uma solicitação maliciosa (espero que não) e seu código se comportar mal, você deseja que seu aplicativo tenha todas as defesas possíveis para limitar os danos. Portanto, acreditamos que sua empresa está implementando políticas de segurança não apenas porque não confia em você ou em seu código, mas também para se proteger de códigos externos maliciosos.
O princípio da autorização mínima afirma que as permissões mínimas exigidas pelo código devem ser concedidas no menor tempo possível. Dito isso, erga sempre o maior número possível de paredes protetoras em torno do seu código. Quando algo ruim acontece – como garante a Lei de Murphy – você ficará feliz por essas paredes protetoras estarem instaladas. Portanto, aqui estão alguns métodos específicos para executar código usando o princípio de autorização mínima.
Escolha um ambiente seguro para o código do seu servidor que permita apenas acesso aos recursos necessários para realizar seu trabalho. Se algumas partes do seu código exigirem permissões altas, considere isolar essa parte do código e executá-la separadamente com permissões mais altas. Para separar com segurança esse código executado com diferentes informações de autenticação do sistema operacional, é melhor executar esse código em um processo separado (executando em um ambiente seguro com privilégios mais elevados). Isso significa que você precisará de comunicação entre processos (como comunicação remota COM ou Microsoft .NET) e precisará projetar a interface para esse código para minimizar viagens de ida e volta.
Se você separar o código em assemblies em um ambiente .NET Framework, considere os níveis de permissão necessários para cada parte do código. Você descobrirá que é um processo fácil: separe o código que requer privilégios mais altos em um assembly separado que lhe conceda mais privilégios, enquanto deixa a maioria dos assemblies restantes rodando com privilégios mais baixos para que você possa adicionar mais proteções ao seu código. Ao fazer isso, não esqueça que, por causa da pilha CAS (Code Access Security), você está limitando as permissões não apenas do seu próprio assembly, mas também de qualquer assembly que você chamar.
Muitas pessoas criam seus próprios aplicativos para que novos componentes possam ser conectados a seus produtos depois de testados e disponibilizados aos clientes. Proteger esse tipo de aplicativo é muito difícil porque não é possível testar todos os caminhos de código possíveis para encontrar bugs e falhas de segurança. Porém, se sua aplicação estiver hospedada, o CLR oferece um excelente recurso que pode ser usado para fechar esses pontos de extensibilidade. Ao declarar um objeto de permissão ou um conjunto de permissões e chamar PermitOnly ou Deny, você adiciona uma marca à sua pilha que bloqueará a concessão de permissões a qualquer código que você chamar. Fazendo isso antes de chamar um plug-in, você pode limitar as tarefas que o plug-in pode executar. Por exemplo, um plugin para cálculo de parcelamento não requer nenhum acesso ao sistema de arquivos. Este é apenas mais um exemplo de privilégio mínimo, em que você se protege de antemão. Certifique-se de observar essas restrições e esteja ciente de que plug-ins com privilégios mais altos podem usar instruções Assert para evitar essas restrições.
8. Esteja ciente dos padrões de falha
e aceite-os. Outros odeiam escrever código de tratamento de erros tanto quanto você. Existem muitos motivos pelos quais o código pode falhar e pode ser frustrante só de pensar neles. A maioria dos programadores, inclusive nós, prefere focar no caminho de execução normal. É aí que o trabalho realmente é feito. Vamos fazer esse tratamento de erros da maneira mais rápida e indolor possível e, em seguida, passar para a próxima linha do código real.
Infelizmente, essa emoção não é segura. Em vez disso, precisamos prestar mais atenção aos padrões de falha no nosso código. Esse código geralmente é escrito com pouca atenção aprofundada e muitas vezes não é totalmente testado. Lembra da última vez que você teve certeza absoluta de que havia depurado cada linha de código de uma função, incluindo cada pequeno manipulador de erros contido nela?
Código não testado geralmente leva a vulnerabilidades de segurança. Existem três coisas que podem ajudá-lo a mitigar esse problema. Primeiro, dê a esses pequenos manipuladores de erros a mesma atenção que seu código normal. Considere o estado do sistema quando o código de tratamento de erros é executado. O sistema está em um estado eficiente e seguro? Segundo, depois de escrever uma função, percorra-a e depure-a completamente algumas vezes, certificando-se de testar cada manipulador de erros. Observe que mesmo com tais técnicas, erros de temporização muito sutis podem não ser descobertos. Pode ser necessário passar um parâmetro de erro para sua função ou ajustar o estado do sistema de alguma forma para permitir a execução do manipulador de erros. Ao dedicar algum tempo para percorrer seu código, você pode desacelerar e ter tempo suficiente para ver seu código e o estado do sistema enquanto ele é executado. Ao percorrer cuidadosamente o código do depurador, descobrimos muitas falhas em nossa lógica de programação. Esta é uma tecnologia comprovada. Por favor, use esta técnica. Por fim, certifique-se de que seu conjunto de testes faça com que sua função falhe. Tente ter um conjunto de testes que examine cada linha de código da função. Isso pode ajudá-lo a identificar padrões, especialmente ao automatizar seus testes e executá-los sempre que criar seu código.
Há uma coisa muito importante a dizer sobre os modos de falha. Certifique-se de que seu sistema esteja no estado mais seguro possível quando seu código falhar. Alguns dos códigos problemáticos são mostrados abaixo:
bool accessGranted = true; // Muito otimista!
tentar {
// Veja se conseguimos acessar c:test.txt
novo FileStream(@"c:test.txt",
FileMode.Open,
ArquivoAccess.Read).Close();
}
catch(SecurityException x) {
//Acesso negado
acesso concedido = falso;
}
pegar (...) {
//Algo mais aconteceu
}
Mesmo usando o CLR, ainda temos permissão para acessar o arquivo. Nesse caso, uma SecurityException não é lançada. Mas e se, por exemplo, a Lista de Controle de Acesso Discricionário (DACL) do arquivo não nos permitir acesso? Neste momento, outro tipo de exceção será lançado. Mas devido às suposições otimistas da primeira linha do código, nunca saberemos disso.
A melhor maneira de escrever este código é ser cauteloso:
bool accessGranted = false; // Seja cauteloso!
tentar {
// Veja se conseguimos acessar c:test.txt
novo FileStream(@"c:test.txt",
FileMode.Open,
ArquivoAccess.Read).Close();
// Se ainda estamos aqui, ótimo!
acesso concedido = verdadeiro;
}
catch (...) {}
Isso será mais estável porque não importa o quanto falhemos, sempre voltaremos ao modo mais seguro.
9. A representação é altamente vulnerável
Ao escrever aplicativos de servidor, você frequentemente se verá usando, direta ou indiretamente, um recurso útil do Windows chamado representação. A representação permite que cada thread de um processo seja executado em um ambiente de segurança diferente, normalmente o do cliente. Por exemplo, quando um redirecionador de sistema de arquivos recebe uma solicitação de arquivo pela rede, ele autentica o cliente remoto, verifica se a solicitação do cliente não viola a DACL no compartilhamento e, em seguida, anexa o sinalizador do cliente ao thread que trata a solicitação . para simular o cliente. Esse thread pode então acessar o sistema de arquivos local no servidor usando o ambiente de segurança do cliente. Isto é conveniente porque o sistema de arquivos local já é seguro. Ele executa uma verificação de acesso levando em consideração o tipo de acesso solicitado, a DACL do arquivo e o sinalizador de representação no thread. Se a verificação de acesso falhar, o sistema de arquivos local reportará isso ao redirecionador do sistema de arquivos, que então enviará um erro ao cliente remoto. Isto é sem dúvida conveniente para o redirecionador do sistema de arquivos, pois ele simplesmente passa a solicitação para o sistema de arquivos local e permite que ele faça suas próprias verificações de acesso, como se o cliente fosse local.
Tudo isso é muito bom para um gateway simples como um redirecionador de arquivos. Mas as simulações são frequentemente utilizadas em outras aplicações mais complexas. Tomemos como exemplo uma aplicação web. Se você escrever um programa ASP não gerenciado clássico, extensão ISAPI ou aplicativo ASP.NET e tiver a seguinte especificação em seu arquivo Web.config
<identity impersonate='true'>
então seu ambiente em execução terá dois ambientes de segurança diferentes: Você terá um tag de processo e uma tag de thread De modo geral, a tag de thread será usada para verificação de acesso (veja a Figura 3). Supondo que você esteja escrevendo um aplicativo ISAPI que seja executado em um processo de servidor web e supondo que a maioria das solicitações não seja autenticada, sua tag de thread poderá ser IUSR_MACHINE, mas sua tag de processo será SYSTEM! Suponha que seu código possa ser explorado por um malfeitor por meio de um buffer overflow. Você acha que ele ficará satisfeito apenas rodando como IUSR_MACHINE? Claro que não. Seu código de ataque provavelmente chama RevertToSelf para remover o sinalizador de representação na esperança de aumentar seu nível de privilégio. Neste caso, ele terá sucesso facilmente. Ele também pode chamar CreateProcess. Ele não copia a tag do novo processo da tag de representação, mas da tag do processo para que o novo processo possa ser executado como SYSTEM.
Então, como resolver esse probleminha? Além de garantir que não ocorram buffer overflows, lembre-se do princípio da autorização mínima. Se o seu código não requer privilégios tão grandes quanto SYSTEM, não configure seu aplicativo web para ser executado em um processo de servidor web. Se você simplesmente configurar seu aplicativo Web para ser executado em um ambiente de isolamento médio ou alto, sua tag de processo será IWAM_MACHINE. Na verdade, você não tem nenhuma permissão, então esse ataque quase não tem efeito. Observe que no IIS 6.0 (que em breve será um componente do Windows .NET Server), o código escrito pelo usuário não será executado como SYSTEM por padrão. Com base no entendimento de que os desenvolvedores cometem erros, qualquer ajuda que o servidor web possa fornecer para reduzir as permissões dadas ao código é útil caso haja um problema de segurança no código.
Aqui está outra armadilha que os programadores COM podem encontrar. COM tem uma tendência ruim de ignorar threads. Se você chamar um servidor COM em processo e seu modelo de thread não corresponder ao modelo do thread de chamada, o COM executará a chamada em outro thread. COM não propaga o sinalizador de representação no thread do chamador, portanto, o resultado é que a chamada é executada no contexto de segurança do processo, e não no contexto de segurança do thread de chamada. Que surpresa!
Aqui está outro exemplo das armadilhas da simulação. Suponha que seu servidor aceite solicitações enviadas por meio de pipes nomeados, DCOM ou RPC. Você autentica clientes e os representa, abrindo objetos do kernel em nome deles por meio da representação. E você esqueceu de fechar um dos objetos (como um arquivo) quando o cliente foi desconectado. Quando o próximo cliente chega, você o autentica e se faz passar por ele, e adivinha o que acontece? Você ainda poderá acessar arquivos que foram "perdidos" pelo cliente anterior, mesmo que o novo cliente não tenha obtido acesso ao arquivo. Por razões de desempenho, o kernel só executa verificações de acesso em um objeto na primeira vez que ele é aberto. Você ainda poderá acessar esse arquivo mesmo se alterar posteriormente seu ambiente de segurança porque está se passando por outro usuário.
As situações mencionadas acima servem para lembrar que a simulação oferece comodidade aos desenvolvedores de servidores, mas essa comodidade traz grandes perigos ocultos. Ao executar um programa com um sinalizador simulado, preste muita atenção ao seu código.
10. Escreva aplicativos que usuários não administradores possam realmente usar.
Isso é realmente um corolário do princípio da autorização mínima. Se os programadores continuarem a desenvolver código que requer um administrador para ser executado corretamente no Windows, não podemos esperar melhorar a segurança do sistema. O Windows possui um conjunto muito sólido de recursos de segurança, mas os usuários não podem tirar proveito deles se precisarem ser administradores para operá-los.
Como você pode melhorar? Primeiro, experimente você mesmo, sem executá-lo como administrador. Você logo aprenderá a dor de usar um programa que não foi projetado pensando na segurança. Um dia, eu (Keith) instalei um software fornecido pelo fabricante do meu dispositivo portátil que sincronizava dados entre meu computador desktop e meu dispositivo portátil. Como de costume, saí da minha conta de usuário normal, efetuei login novamente usando a conta de administrador integrada, instalei o software, efetuei login novamente na minha conta normal e tentei executar o software. Como resultado, o aplicativo exibe uma caixa de diálogo informando que um arquivo de dados necessário não pode ser acessado e, em seguida, exibe uma mensagem de violação de acesso. Amigos, este é o produto de software de um grande fabricante de dispositivos portáteis. Existe alguma desculpa para esse erro?
Depois de executar o FILEMON em http://sysinternals.com (em inglês), descobri rapidamente que o aplicativo estava tentando abrir um arquivo de dados para acesso de gravação que estava instalado no mesmo diretório do centro executável do aplicativo. Quando os aplicativos são instalados no diretório Arquivos de Programas conforme esperado, eles não devem tentar gravar dados nesse diretório. Os Arquivos de Programas têm uma política de controle de acesso tão restritiva por um motivo. Não queremos que os usuários gravem nesses diretórios, pois isso tornaria mais fácil para um usuário deixar um Trojan para outro usuário executar. Na verdade, esta convenção é um dos requisitos básicos de assinatura do Windos XP (consulte http://www.microsoft.com/winlogo [Inglês]).
Ouvimos muitos programadores dando desculpas sobre por que optam por executar como administrador ao desenvolver código. Se continuarmos a ignorar este problema, só pioraremos as coisas. Amigos, vocês não precisam de direitos de administrador para editar um arquivo de texto. Direitos de administrador também não são necessários para editar ou depurar um programa. Quando você precisar de privilégios de administrador, use o recurso RunAs do sistema operacional para executar 玎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [Inglês]).
Ouvimos muito isso Programadores dão desculpas por que eles optam por executar como administrador ao desenvolver código Se continuarmos a ignorar esse problema, isso só piorará as coisas. A edição de um arquivo de texto não requer privilégios de administrador. Se você estiver escrevendo ferramentas para desenvolvedores, terá uma responsabilidade adicional por esse grupo. acabar com esse ciclo vicioso de escrever código que só pode ser executado como administrador. O objetivo precisa mudar fundamentalmente.
Para obter mais informações sobre como os desenvolvedores podem executar facilmente como não-administradores, consulte o site de Keith em http://www.develop. com/kbrown (em inglês). Confira o livro Writing Secure Code de Michael (Microsoft Press, 2001), que fornece dicas sobre como escrever aplicativos que funcionam bem em um ambiente sem administrador.