Seguridad del sistema de archivos

Cap铆tulo 26. Seguridad del sistema de archivos

PHP est谩 sujeto a la seguridad misma de la mayor铆a de sistemas de servidores en lo que a permisos sobre archivos y directorios se refiere. Esto le permite controlar cu谩les archivos en el sistema de archivos pueden ser le铆dos. Debe tenerse cuidado con aquellos archivos que tengan permisos de lectura globales, para asegurarse de que su contenido es seguro y no represente peligro el que pueda ser le铆do por todos los usuarios con acceso al sistema de archivos.

Ya que PHP fue dise帽ado para permitir acceso al nivel de usuarios al sistema de archivos, es completamente posible escribir un script PHP que le permita leer archivos del sistema como /etc/passwd, modificar sus conexiones tipo ethernet, enviar trabajos de impresi贸n masivos, etc. Esto tiene algunas implicaciones obvias, en el sentido en que usted tiene que asegurarse de que los archivos desde lo que lee y hacia los que escribe datos, sean los correctos.

Considere el siguiente script, en donde un usuario indica que quisiera eliminar un archivo ubicado en su directorio personal. Este caso asume que se trata de una situaci贸n en donde se usa normalmente una interfaz web que se vale de PHP para la gesti贸n de archivos, as铆 que el usuario de Apache tiene permitido eliminar archivos en los directorios personales de los usuarios.

Ejemplo 26-1. Un chequeo pobre de variables nos lleva a...

<?php
// eliminar un archivo del directorio personal del usuario

$nombre_usuario = $_POST['nombre_enviado_por_el_usuario'];
$directorio     = "/home/$nombre_usuario";

$archivo_a_eliminar = "$archivo_de_usuario";

unlink ("$directorio/$archivo_de_usuario");

echo
"&iexcl;El archivo $archivo_a_eliminar ha sido eliminado!";
?>
Ya que el nombre de usuario es enviado desde un formulario de usuario, cualquiera puede enviar un nombre de usuario y archivo propiedad de otra persona, y eliminar archivos. En este caso, usted querr谩 usar otro m茅todo de autenticaci贸n. Considere lo que sucede si las variables enviadas son "../etc/" y "passwd". El c贸digo entonces se ejecutar铆a efectivamente como:

Ejemplo 26-2. ... un ataque al sistema de archivos

<?php
// elimina un archivo de cualquier parte del disco duro al que el
// usuario de PHP tiene acceso. Si PHP tiene acceso de root:

$nombre_usuario = "../etc/";
$directorio     = "/home/../etc/";

$archivo_a_eliminar = "passwd";

unlink ("/home/../etc/passwd");

echo
"&iexcl;El archivo /home/../etc/passwd ha sido eliminado!";
?>
Hay dos importantes medidas que usted debe tomar para prevenir estas situaciones.

Aqu铆 hay una versi贸n mejorada del script:

Ejemplo 26-3. Un chequeo de nombres de archivos m谩s seguro

<?php
// elimina un archivo de cualquier parte del disco duro al que el
// usuario de PHP tiene acceso.

$nombre_usuario = $_SERVER['REMOTE_USER']; // uso de un mecanismo de
                                           // autenticacion

$directorio = "/home/$nombre_usuario";

$archivo_a_eliminar = basename("$archivo_de_usuario"); // remover rutas
unlink ($directorio/$archivo_a_eliminar);

$fp = fopen("/home/registros/eliminacion.log","+a"); // registrar el proceso

$cadena_de_registro = "$nombre_usuario $directorio $archivo_a_eliminar";

fwrite ($fp, $cadena_de_registro);
fclose($fp);

echo
"&iexcl;El archivo $archivo_a_eliminar ha sido eliminado!";
?>
Sin embargo, incluso este caso no est谩 libre de problemas. Si su sistema de autenticaci贸n le ha permitido a los usuarios la creaci贸n de sus propios nombres en el sistema, y un usuario elige "../etc/", el sistema se encuenrta nuevamente expuesto. Por esta raz贸n, puede que uster prefiera escribir un chequeo m谩s personalizado:

Ejemplo 26-4. Chequeo de nombres de archivos aun m谩s seguro

<?php
$nombre_usuario
= $_SERVER['REMOTE_USER']; // uso de un mecanismo de
                                           // autenticacion

$directorio = "/home/$nombre_usuario";

if (!
ereg('^[^./][^/]*$', $archivo_de_usuario))
     die(
'nombre de archivo inv&aacute;lido'); // finalizar,
                                               // no ejecutar el proceso

if (!ereg('^[^./][^/]*$', $nombre_usuario))
     die(
'nombre de archivo inv&aacute;lido'); // finalizar,
                                               // no ejecutar el proceso

//etc...
?>

Dependiendo de su sistema operativo, existe una amplia variedad de archivos sobre los que usted deber铆a estar atento, incluyendo las entradas de dispositivos (/dev/ o COM1), archivos de configuraci贸n (archivos /etc/ y los archivos .ini), areas conocidas de almacenamiento de datos (/home/, Mis Documentos), etc. Por esta raz贸n, usualmente es m谩s sencillo crear una pol铆tica en donde se proh铆ba toda transacci贸n excepto por aquellas que usted permita expl铆citamente.