PHP: include seguros

Me canso de ver a menudo aplicaciones web o scripts que hacen cosas como esta:

<?PHP
$sPagina = $_GET[‘pagina’];
include ($sPagina);
?>

Eso es lo que yo llamo una aberración de seguridad. Al menos algunos se toman el trabajo de “limpiar” $sPagina, igualmente para mi gusto, sigue siendo peligroso por el solo hecho de estar mostrando algo de la estructura interna de nuestra aplicación.

La idea es que, cuando quieras incluir una “página” o archivo dentro de tu script “a petición”, es que trates de darle la menor cantidad de información posible a quién está del otro lado (cliente). Hay muchos métodos, pero básicamente te recomiendo dos: utilizar un diccionario, o codificando la petición.

Utilizando un diciconario

En este caso te valés de un array. Este ejemplo es para cuando tenés un número conocido de páginas.

<?PHP
// Tienes el array con la lista de páginas a cargar
$aPaginas = array(0 => 'index.php', 1 => 'acerca-de.php', 2 => 'servicios.php', 3 => contacto.php);
// Tomas el parámetro "pagina" que viene por GET y lo transformas a un entero
$iPagina = (int) $_GET['pagina'];
// Validación
if (!isset($aPaginas[$iPagina])
{
// La petición es incorrecta. Están tratando de cargar una página que no existe
// Puedes directamente "matar" la petición con un die('Solicitud incorrecta'); o mandarlos
// a la página por defecto: index.php que está en el índice 0;
$iPagina = 0;
}
// La petición es válida
include($aPaginas[$iPagina];
?>

Codificando la petición

El ejemplo es para cuando no tenés un N° conocido de páginas, así que lo mejor es codificar de alguna forma (más simple o más compleja) la petición. La idea es que los archivos a incluir los tengas en un directorio, y bajo este, los directorios que quieras. De esta forma te asegurás de incluir solo archivos de ese lugar (directorio base).

Cuando me refiero a codificar no me refiero a “encriptar” (aunque se puede hacer) sino más que nada a ocultar detalles de la implementación. Por ejemplo evitar peticiones de path’s absolutos a nuestros archivos para que no conozcan los directorios, el document root, y demás.

Supongamos que tenemos nuestros includes organizados en:

/fichas/rubro01/producto0001.php
/fichas/rubro02/producto0001.php
/fichas/rubro03/producto0001.php
/fichas/rubro03/producto0002.php

Sabemos entonces que, todos nuestros includes, están organizados dentro del /fichas. También tenemos un patrón en el nombre de la segunda carpeta, donde el nombre es igual a “rubro” + NN (código del rubro). Y la ficha en sí, que está dentro de la carpeta rubroNN; tiene la forma “producto” + NNNN .php

Entonces, una forma de ocultar nuestra implementación es utilizando estos patrones, y recibiendo como petición solamente el código del rubro y del producto, datos suficientes para “deducir” que ficha queremos incluir en nuestro script.

Podríamos usar dos parámetros: rubro y producto, pero mejor usamos uno solo y separamos el rubro del producto con un caracter, por ejemplo el “.” (punto). Así, nuestro request va a ser pagina=1.1 (rubro 1, producto 1).

<?PHP
// Este es el directorio base
$sDirBase = '/fichas';

// Recuperamos la página que se pide
$sPagina = (string) $_GET['pagina'];

// Siempre en $sPagina esperamos el código del rubro más el código del producto separados por un punto. Validamos este patrón con una expresión regular.
if (preg_match("/^\d{1,2}\.\d{1,4}$/", $sPagina))
{
// La expresión regular validó. Proceso el request $sPagina
$aPagina = explode('.', $sPagina);
// Armo el nombre de nuestro archivo a incluir
$sPagina = '/rubro' . str_pad($aPagina[0], 2, '0', STR_PAD_LEFT) .
'/producto' . str_pad($aPagina[1], 4, '0', STR_PAD_LEFT) .
'.php';
// Listo... ahora lo incluyo con toooda seguridad :)
include ($sDirBase . $sPagina);
}
else
{
// Falló la validación de la expresión regular
}

?>

Es muy simple y muy fácil. Y muy seguro. Espero que sirva.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *