Archivado en la categoría (Identidad Digital, PAPI) por Daniel
Publicado el 11-03-2010

Apache, como consecuencia de la vulnerabilidad CVE-2003-0020, escapa todos los logs de errores, con el fin de evitar que un atacante pueda insertar secuencias de caracteres de escape, y provocar vulnerabilidades en emuladores de terminal.
Pues bien esto hace que todos los caracteres de control sean impresos en los ficheros de log de errores con su representación visual (i. e. ‘\n’, \t’, etc), en vez de  actuar como tales. Por lo que depurar mensajes de error generados por cualquier componente externo a Apache, como por ejemplo aplicaciones PHP, scripts-cgi o handles mod_perl, es muy poco agradable, ya que te encuentras con mensajes de error de 10 o 15 líneas condensadas en una sola.

Como en mi día a día tengo que tratar con PAPI (desarrollado en mod_perl2) sufro bastante esta desconsideración de Apache al escribir los mensajes de debug que genera PAPI, vamos que tengo que manejar mensajes de debug de PAPI con el siguiente formato:

[Thu Mar 11 11:19:52 2010] [debug] ApachePoA.pm(105): [client 130.206.6.37]
 PAPI-DEBUG#20069_1268302792_sirgpoa: Parameters initialized:$VAR1 = bless( {\n
                    'Filtered' => 1,\n                 'Accept_File' =>
/aaaa/bbbb/cccc/dddd.ddddd/conf/papi/gpoa/shim.gif',\n                 
'String' => /GPoA',\n                 'attrList' => [],\n                 'filte
rs' => [],\n                 'PxCkSize' => 320768,\n                 ApacheReq
uest' => bless( do{\\(my $o = 11921096)}, 'Apache2::RequestRec' ),\n    'useHcoo
k' => 0,\n                 'Type' => 'Location',\n                 'Hcook_Ha
ndler' => undef,\n                 'registerKey' => '',\n                 'MxNonc
eErr' => 3,\n                 'PoARw' => [\n                              {\n              
                 'value' => 'check=false,$1,',\n

Lo que como se puede ver es inmanejable. Por esto un día decidí hacer uso de la capacidad que tiene el Apache para enviar los logs a un programa externo que recibirá estos logs por entrada estándar. Siendo en este programa externo donde reemplazo los caracteres de control (i.e. su representación visual) por su valor. Con lo que consigo log de debug de PAPI con el siguiente formato:

[Wed Mar 10 15:51:52 2010] [debug] ApachePoA.pm(105): [client 130.206.6.51]
PAPI-DEBUG#29900_1268232712_simplepoa: Parameters initialized:
$VAR1 = bless( {
                 'Filtered' => 1,
                 'Accept_File' => '/etc/apache2/papi/global/images/image_ok.png',
                 'String' => '/simplePoA',
                 'attrList' => [],
                 'filters' => [
                                {
                                  'reg' => '.*',
                                  'act' => 'accept'
                                }
                              ],
                 'PxCkSize' => 320768,
                 'ApacheRequest' => bless( do{\\(my $o = 155158008)},
                                              'Apache2::RequestRec' ),
                 'useHcook' => '1',
                 'Type' => 'Location',
                 'registerKey' => '',
                 'Hcook_Handler' => '.*',
                 'MxNonceErr' => 3,
                 'URL_Timeout' => '600',
                 'PoARw' => [],

Una mejora considerable ¿no?

El truco que hay que hacer consiste en definir el ErrorLog así:

ErrorLog "|/usr/local/bin/splitApacheLog.pl /path/papi_error.log /path/error.log"

Donde el progrma Perl splitApacheLog.pl consiste en:

 1 #!/usr/bin/perl
 2 
 3 my $papi = shift @ARGV;
 4 my $error = shift @ARGV;
 5 my $line = '';
 6 #$|=1;
 7 
 8 open(PAPI_OUT,">>$papi")
 9     or die("No se puede abrir el fichero $papi\n$!\n");
10 open(ERROR_OUT,">>$error")
11    or die("No se puede abrir el fichero $error\n$!\n");
12 
13 while (defined($line = )){
14     if ($line =~ /PAPI/){
15         $line =~ s/\\n/\n/g;
16         do { 
17             use bytes; 
18             my $size=length($line);
19             syswrite(PAPI_OUT, $line, $size);
20         }
21     }else{
22         $line =~ s/\\n/\n/g;
23         do {
24             use bytes;
25             my $size=length($line);
26             syswrite(ERROR_OUT, $line, $size);
27         }
28     }
29 }

Con esto conseguimos dos ficheros de log, uno para PAPI y otro para Apache, como se puede ver discriminamos con la expresión regular de la línea 14 según la cual enviamos la salida al fichero PAPI o al fichero de Apache.
El código es muy simple y intuitivo, por lo que modificarlo para que se ajuste a las necesidades de cualquiera no debe ser muy complicado.

Por último comentar, que se puede compilar Apache sin esta característica tan molesta de los mensajes de log escapados. Para ello sólo debéis hacer uso de la directiva de compilación DAP_UNSAFE_ERROR_LOG_UNESCAPED, tal y como os muestro a continuación:

CFLAGS=-DAP_UNSAFE_ERROR_LOG_UNESCAPED ./configure

Obviamente no recomiendo esta práctica, sólo informo de ella.

Escribe un comentario

You must be logged in to post a comment.