Tradução livre de:
Intrusion Detection For PHP Applications With PHPIDS
Este tutorial explica como configurar o PHPIDS em um servidor web com Apache2 e PHP5. PHPIDS (PHP Intrusion Detection System) é um IDS simples de usar, bem estruturado, rápido, possui camada de segurança para seu aplicativo web baseado em PHP. O PHPIDS simplesmente reconhece quando um atacante tenta quebrar o seu site e reage exatamente da maneira que você quer. Baseado em um conjunto testado e aprovado de regras de filtragem onde qualquer ataque é atribuído a uma classificação numérica de impacto, que torna mais fácil decidir que tipo de ação deve seguir a tentativa de intrusão.
1. Definições
Eu testei isso em um sistema LAMP Debian Lenny com Apache2 e PHP5 no IP 192.168.0.100. O usuário e grupo do Apache no Debian Lenny é www-data, portanto, se você estiver em uma distribuição diferente, o usuário do Apache e grupo pode ser diferente. A localização do php.ini (/etc/php5/apache2/php.ini no Debian Lenny) pode ser diferente também.
Estou usando uma máquina virtual com o documento raiz /var/www/web1/web neste exemplo.
2. Instalação do PHPIDS
Por razões de segurança eu quero instalar PHPIDS fora da raiz do documento, assim que eu criei o diretório /var/www/web1/phpids:
# mkdir /var/www/web1/phpids
Então vou instalar o PHPIDS (no momento da redação deste artigo a última versão é a 0.4.7). De todo o conteúdo do arquivo PHPIDS-0.4.7.tar.gz, só precisamos do diretório lib/:
# cd /tmp
# wget http://php-ids.org/files/phpids-0.4.7.tar.gz
# tar xvfz PHPIDS-0.4.7.tar.gz
# cd PHPIDS-0.4.7
# mv lib /var/www/web1/phpids/
Agora eu vou mudar para o diretório /var/www/web1/phpids/lib/IDS...
# cd var/www/web1/phpids/lib/IDS
... e fazer o diretório tmp (que irá conter o arquivo de log PHPIDS) gravável para o usuário e grupo do Apache:
# chown -R www-data:www-data tmp/
Em seguida vamos configurar o arquivo de configuração PHPIDS (Config.ini):
# cd Config/
# vi Config.ini
Estou usando a configuração padrão aqui, tudo que eu fiz foi ajustar os caminhos:
; PHPIDS Config.ini
; General configuration settings
; !!!DO NOT PLACE THIS FILE INSIDE THE WEB-ROOT IF
; DATABASE CONNECTION DATA WAS ADDED!!!
[General]
filter_type = xml
filter_path = /var/www/web1/phpids/lib/IDS/default_filter.xml
tmp_path = /var/www/web1/phpids/lib/IDS/tmp
scan_keys = false
exceptions[] = __utmz
exceptions[] = __utmc
; If you use the PHPIDS logger you can define specific configuration here
[Logging]
; file logging
path = /var/www/web1/phpids/lib/IDS/tmp/phpids_log.txt
; email logging
; note that enabling safemode you can prevent spam attempts,
; see documentation
recipients[] = test@test.com.invalid
subject = "PHPIDS detected an intrusion attempt!"
header = "From: info@php-ids.org"
safemode = true
allowed_rate = 15
; database logging
wrapper = "mysql:host=localhost;port=3306;dbname=phpids"
user = phpids_user
password = 123456
table = intrusions
; If you would like to use other methods than file caching you can configure them here
[Caching]
; caching: session|file|database|memcached|none
caching = file
expiration_time = 600
; file cache
path = /var/www/web1/phpids/lib/IDS/tmp/default_filter.cache
; database cache
wrapper = "mysql:host=localhost;port=3306;dbname=phpids"
user = phpids_user
password = 123456
table = cache
; memcached
;host = localhost
;port = 11211
;key_prefix = PHPIDS
;tmp_path = /var/www/web1/phpids/lib/IDS/tmp/memcache.timestamp
Agora, quando você chamar o arquivo em um navegador (http://192.168.0.100/phpids.php por exemplo), verá uma página em branco. Mas se você tentar anexar alguns parâmetros maliciosos para a URL (ex. http://192.168.0.100/phpids.php?test=%22%3EXXX%3Cscript%3Ealert(1)%3C/script%3E), PHPIDS irá detectar este ataque e imprimir as suas conclusões no navegador:
Agora temos que encontrar uma maneira de fazer com que nossos scripts PHP se utilizem do PHPIDS. Claro, você não quer modificar todos os seus scripts PHP (pode ter centenas deles ...). Felizmente há uma maneira melhor: podemos dizer ao PHP para preceder um script PHP sempre que um script PHP é chamado. Por exemplo, se nós chamamos o script info.php em um navegador, o PHP executará phpids.php e depois info.php, e nós nem sequer temos que modificar info.php.
Podemos fazer isso usando o parâmetro
auto_prepend_file do PHP. Podemos definir isso em nosso
php.ini (esta é uma configuração global que é válida para todos os sites PHP no servidor), ou em um arquivo .htaccess (esta é uma configuração válida somente para um site em questão).
O arquivo php.ini
Abra seu php.ini (exemplo /etc/php5/apache2/php.ini) e defina tag auto_prepend_file para var/www/web1/web/phpids.php/:
# vi /etc/php5/apache2/php.ini
[...]
auto_prepend_file = /var/www/web1/web/phpids.php
[...]
Reinicie o Apache:
# /etc/init.d/apache2ctl restart
O arquivo .htaccess
Em vez de modificar o php.ini (que é uma mudança global, ou seja, a mudança é válida para todos os sites que usam PHP no servidor), você pode usar um arquivo .htaccess (para a definição seria válida apenas para a web site onde você criar o arquivo .htaccess):
# vi /var/www/web1/web/.htaccess
php_value auto_prepend_file /var/www/web1/web/phpids.php
Por favor, certifique-se que o vhost para seu site em /var/www/web1/web contém algo parecido com isto (caso contrário a linha php_value no arquivo .htaccess será ignorada). Se você tiver que alterar o vhost, por favor, não se esqueça de reiniciar o Apache):
AllowOverride All
Agora vamos criar um arquivo PHP simples, em /var/www/web1/web/info.php:
# vi /var/www/web1/web/info.php
phpinfo();
?>
Chame esse arquivo em um navegador (http://192.168.0.100/info.php) e você deve ver o phpinfo() normalmente.
Agora vamos anexar alguns parâmetros maliciosos para a URL (ex. http://192.168.0.100/info.php?test=%22%3EXXX%3Cscript%3Ealert(1)%3C/script%3E). Você deverá encontrar um relatório PHPIDS antes da saída phpinfo() (porque em /var/www/web1/web/phpids.php foi executado antes do /var/www/web1/web/info.php):
Os logs do PHPIDS vão para /var/www/web1/phpids/lib/IDS/tmp/phpids_log.txt, então você deve ver algo no log a partir de agora:
# cat /var/www/web1/phpids/lib/IDS/tmp/phpids_log.txt
"192.168.0.200", 2008-06-04T17:36:08+02:00,54, "xss csrf id rfe lfi", "REQUEST.test=%5C%22%3EXXX%3Cscript%3Ealert%281%29%3C%2Fscript%3E GET.test=%5C%22%3EXXX%3Cscript%3Ealert%281%29%3C%2Fscript%3E", "%2Finfo.php%3Ftest%3D%2522%253EXXX%253Cscript%253Ealert%281%29%253C%2Fscript%253E"
Agora, observando o log, você sabe o que os hackers estão tentando fazer suas aplicações PHP e você pode tentar endurecer as suas aplicações.
Para adicionar outro nível de segurança, podemos parar a execução de nossos scripts PHP se o PHPIDS achar que eles estão sob ataque. Basta adicionar algo que assuste ou bote os intrusos para correr na seção "if (!$result->isEmpty()) {}" em /var/www/web1/web/phpids.php:
# vi /var/www/web1/web/phpids.php
$_REQUEST,
'GET' => $_GET,
'POST' => $_POST,
'COOKIE' => $_COOKIE
);
$init = IDS_Init::init('/var/www/web1/phpids/lib/IDS/Config/Config.ini');
$ids = new IDS_Monitor($request, $init);
$result = $ids->run();
if (!$result->isEmpty()) {
// Take a look at the result object
echo $result;
require_once 'IDS/Log/File.php';
require_once 'IDS/Log/Composite.php';
$compositeLog = new IDS_Log_Composite();
$compositeLog->addLogger(IDS_Log_File::getInstance($init));
$compositeLog->execute($result);
die('Go away!
');
}
?>
Se não houver ataques, os scripts são executados, mas se o PHPIDS encontra um ataque, ele impede que os scripts sejam executados e exibe uma mensagem para os intrusos:
Links para referência:
Bom, é só isso. E não se esqueçam de passar no site do
Backtrack Brasil.
Se tiver algum evento ou palestra para ministramos aí na sua cidade, entrem em contato.
Nós vamos até vocês com o Backtrack Brasil.
Até a próxima!
Mauro Risonho de Paula Assumpção
Fundador do BackTrack Brasil (BTB)
Pentester, Analista em Segurança
Desenvolvedor de Software
firebits@backtrack.com.br
http://www.backtrack.com.br
última palestra ministrada: 05-fev-2009 Intel Moblin Day 2008-2009
http://www.intel.com/portugues/pressroom/releases/2009/0205.htm