Back-End

27 jun, 2008

Resolvendo dois problemas num só patch

Publicidade

Nos últimos meses, na empresa onde trabalho, estive um pouco atarefado com problemas de invasão do tipo SQL Injection. Eu já tinha avisado meus superiores quando a vulnerabilidade do sistema que estava em utilização, porém, minhas sugestões para tornar o sistema mais seguro, só foram colocadas em prática quando o pesadelo virou realidade. A partir de então, o aspecto segurança passou a ser visto com mais atenção.

Após ganhar algumas horas em pesquisa sobre SQL Injection, descobri que o principal fator que possibilita o sucesso de um ataque deste tipo é a famosa técnica que a grande maioria de administradores inexperientes conhece, chamada NNF (next, next, finish). Achou engraçado? Mas é isso mesmo. A pessoa instala um servidor com as configurações padrão e logo disponibiliza este servidor em produção sem aplicar nenhum procedimento de segurança. Infelizmente isso é o que mais acontece e aconteceu comigo (não sou administrador de redes nem especialista em segurança, mas pra variar, em TI é sempre bom conhecer um pouco de tudo).

A instalação padrão de um servidor IIS exibe os erros HTTP 500;100 para o usuário que acessa um site. Esta configuração é tudo que um cracker precisa para desenvolver seu código malicioso. As formas mais comuns, através de formulários e até mesmo query strings, propositalmente o invasor vai provocando erros no código que fornecem as informações que ele precisa.

Sendo assim a melhor forma de tentar impedir o sucesso desse tipo de ataque é não exibir os erros http 500;100. Então você cria uma página de erro personalizada e direciona o erro para página criada na guia Http Custom Errors das propriedades do site.

Esta não é a solução definitiva, porém, somente esta ação aparentemente insignificante, reduz drasticamente a possibilidade de sucesso de um SQL Injection. Quer saber? Muito raramente alguém faz isso (com a exceção de empresas especializadas no assunto, ou empresas que foram vítimas de ataques).

Outra medida a ser tomada, e não menos importante, é a correção das falhas de segurança do código que em conjunto com os erros exibidos na tela, permitiram o sucesso da invasão. Mas isso eu vou mostrar falar em outro artigo.

Acontece que com a customização do erro, eu eliminei a única forma de debug que eu e os outros desenvolvedores que trabalham comigo tínhamos. Nesta hora de desespero total, eu percebi que ainda existia uma luz no fim do túnel: estou falando do comando Server.GetLastError().

Com esse comando é possível criar uma página de erro personalizada. Só que mostrar os erros na tela voltaria a fornecer informações preciosas aos invasores. Então tive a idéia de enviar os erros por e-mail. Pensei em algumas informações que os desenvolvedores precisam saber para resolver o problema e desenvolvi um script que reúne as seguintes informações: url de origem, url de destino, página do erro, código e descrição do erro, variáveis de formulário (post), variáveis de query string (get), variáveis de sessão do usuário e todos as ServerVariables. Adicionei este script a pagina de erro customizado e, acreditem ou não, uma série de melhorias começaram a acontecer:

  • os desenvolvedores passaram a ter informações antes mesmo de o usuário abrir um chamado junto à equipe de suporte;
  • conhecimento de bugs no sistema, antes desconhecidos;
  • redução do número de abertura de chamados;
  • usuários mais satisfeitos com o sistema;
  • um sistema mais estável e seguro (porque as tentativas de invasão mal sucedidas também chegam por e-mail), entre outras coisas;

A principal desvantagem é a quantidade de e-mails recebidos inicialmente, porém, na medida que os problemas são solucionados, obviamente os e-mails diminuem.

Os famosos robôs de indexação de páginas (os crawlers como Googlebot, msnbot…), na tentativa de atualizar seu cache de página antigas aumentavam bastante o número de e-mails. Por isso existe no script um filtro dos principais robôs que acessam as páginas do meu sistema.

Segue o código do script que salvei com o nome my500.asp (esta página deve ser configurada no IIS) :

my500.asp


<%LANGUAGE="VBSCRIPT" CODEPAGE="1252"%>
<%
' TRATAMENTO DE ERROS
Option Explicit
On Error Resume Next
Response.Clear
Dim objError, sKey, Texto
Set objError = Server.GetLastError()

' EXPRESSÃO REGULAR PARA IDENTIFICAR SE O AGENTE É UM ROBÔ (CRAWLERs)
Dim reBots
Set reBots = new RegExp
reBots.IgnoreCase = True
reBots.Global = True
reBots.Pattern = "(msnbot|Slurp|Googlebot)"
Texto = Request.ServerVariables("HTTP_USER_AGENT")

' SE NÃO FOR UM ROBÔ, ENVIA O E-MAIL
If Not reBots.Test( Texto ) Then

	' LÊ O ARQUIVO CORPO DO E-MAIL (MODELO)
	Dim fso, HtmlFile, HtmlText
	Set fso 	 = Server.CreateObject("Scripting.FileSystemObject")
	Set HtmlFile = fso.OpenTextFile(Server.MapPath("/erros/my500.html"), 1, False)
	HtmlText 	 = HtmlFile.ReadAll
	HtmlFile.Close
	Set HtmlFile = Nothing
	Set fso 	 = Nothing
	
	' SUBSTITUI VARIÁVEIS DO CORPO DO E-MAIL
	Dim re, sd
	Set sd = Server.CreateObject("Scripting.Dictionary")
	Set re = new RegExp
	re.IgnoreCase = True
	re.Global	  = True
	
	' ARRAY DE VALORES QUE SERÃO ADICIONADOS AO CORPO DO TEXTO
	Texto = "http://" & Request.ServerVariables("HTTP_HOST") & Request.ServerVariables("URL")
	If Len(Trim( Request.ServerVariables("QUERY_STRING") )) <> 0 Then Texto = Texto  "?"  Request.ServerVariables("QUERY_STRING")				
	sd.Add "host", Texto
	
	Texto = Request.ServerVariables("HTTP_REFERER")
	If Len(Trim(Texto)) = 0 Then Texto = "Acesso Direto"
	sd.Add "referer", Texto
	
	sd.Add "iis.err", 	objError.ASPCode
	sd.Add "com.err", 	objError.Number & " (0x" & Hex(objError.Number) & ")"
	sd.Add "err.src", 	objError.Source
	sd.Add "filename",	objError.File
	sd.Add "err.line", 	objError.Line
	sd.Add "err.desc", 	objError.Description
	sd.Add "asp.desc", 	objError.ASPDescription
	
	Texto = ""
	For Each sKey In Request.QueryString 
		Texto = Texto & sKey & " = " & Request.QueryString(sKey) & "<br />" & VbCrLf
	Next
	sd.Add "request.querystring", Texto
	
	Texto = ""
	For Each sKey In Request.Form 
		Texto = Texto & sKey & " = " & Request.Form(sKey) & "<br />"  VbCrLf
	Next
	sd.Add "request.form", Texto
	
	Texto = ""
	For Each sKey In Request.ServerVariables
		Texto = Texto & sKey & " = " & Request.ServerVariables(sKey) & "<br />"  VbCrLf
	Next
	sd.Add "Request.ServerVariables", Texto
	
	Texto = ""
	For Each sKey In Session.Contents 
		Texto = Texto & sKey & " = " & Session.Contents(sKey) & "<br />" & VbCrLf
	Next
	sd.Add "sessions", Texto
	
	' SUBSTITUI AS VARIÁVEIS NO CORPO DO E-MAIL QUE SERÁ ENVIADO
	For Each sKey In sd
		re.Pattern 	= "\{\{" & sKey & "\}\}"
		HtmlText 	= re.Replace( HtmlText, sd( sKey ) )
	Next
	
	sd.RemoveAll
	Set sd = Nothing
	Set re = Nothing
	
	' ENVIA UM E-MAIL PARA O ADMINISTRADOR DO SISTEMA
	Dim objCDOSYSMail
	Dim objCDOSYSCon
	
	Set objCDOSYSMail = Server.CreateObject("CDO.Message")
	Set objCDOSYSCon  = Server.CreateObject("CDO.Configuration")
	
	objCDOSYSMail.From 		= "email_from@seusite.com.br"
	objCDOSYSMail.To   		= "dev@seusite.com.br"
	objCDOSYSMail.Subject  	= "Erro ASP 500"
	objCDOSYSMail.HtmlBody 	= HtmlText
	objCDOSYSMail.Send
	
	' DESTRÓI OS OBJETOS
	Set objCDOSYSMail = Nothing
	Set objCDOSYSCon = Nothing
	
End If

' DESTRÓI OS OBJETOS
Set reBots   = Nothing
Set objError = Nothing
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Erro ASP 500</title>
</head>
<body>
Mensagem de erro que você achar melhor.
</body>
</html>

O script utiliza uma página modelo (my500.html) com variáveis que são substituidas pelo devido conteúdo.

Segue o código da página my500.html:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Erro ASP 500</title>
<style>
body  { FONT-FAMILY: Arial; FONT-SIZE: 10pt; BACKGROUND: ffffff; COLOR: 000000; MARGIN: 15px; }
H2    { FONT-SIZE: 16pt; COLOR: ff0000; }
TABLE { BACKGROUND: FFF; PADDING: 5px; }
</style>
</head>
<body>
<h2 align="center">Erro ASP 500</h2>
<p align="center">Paacute;gina de origem: {{referer}}<br />
Paacute;gina com erro:  {{host}}</p>
<div align="center"><center>
<table width="90" border="0" cellpadding="2" cellspacing="2">
  <tr>
    <th width="180" align="left" valign="top" nowrap>Nuacute;mero do Erro IIS</th>
    <td align="left" valign="top">{{iis.err}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap>Nuacute;mero do Erro COM </th>
    <td align="left" valign="top">{{com.err}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap>Coacute;digo Fonte do erro </th>
    <td align="left" valign="top">{{err.src}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap>Nome do Arquivo </th>
    <td align="left" valign="top">{{filename}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap> Linha </th>
    <td align="left" valign="top">{{err.line}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap>Breve Descricao </th>
    <td align="left" valign="top">{{err.desc}}</td>
  </tr>
  <tr>
    <th width="180" align="left" valign="top" nowrap>Descriccedil;atilde;o Completa </th>
    <td align="left" valign="top">{{asp.desc}}</td>
  </tr>
  <tr>
    <th colspan="2" align="left" valign="top" nowrap>nbsp;</th>
  </tr>
  </table>
  <table width="90" border="0" cellpadding="2" cellspacing="2">
  <tr>
    <th colspan="2" align="left" valign="top" nowrap>Query String</th>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap> 
	{{request.querystring}}</td>
  </tr>
     <tr>
    <th colspan="2" align="left" valign="top" nowrap>nbsp;</th>
  </tr>
  <tr>
    <th colspan="2" align="left" valign="top" nowrap>Formulaacute;rio </th>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap> {{request.form}}</td>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap>nbsp;</td>
  </tr>
  <tr>
    <th colspan="2" align="left" valign="top" nowrap>Variaacute;veis de Sessatilde;o </th>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap>{{sessions}}</td>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap>nbsp;</td>
  </tr>
  <tr>
    <th colspan="2" align="left" valign="top" nowrap>Variaacute;veis do Servidor </th>
  </tr>
  <tr>
    <td colspan="2" align="left" valign="top" nowrap>{{Request.ServerVariables}}</td>
  </tr>
</table>
</center>
</div>
</body>
</html>

Espero que gostem e que o scirpt ajude a muitos, porque a mim tem ajudado bastante.

Abraços.