Consejos de seguridad programando en PHP

En mi último artículo comentaba la falla de seguridad del servicio sharethis.com con su API para compartir enlaces en las redes sociales que detecté al utilizar su servicio en este blog. Les notifiqué el error por medio de feedback y en menos de 48 horas me dieron respuesta, agradeciéndome luego de corregir el error. Esta tipo de cosas son las que los programadores y webmasters deben evitar a toda costa. No solo por que pone en riesgo la aplicación o página web, sino también, la información del usuario que utiliza el servicio.

En vista de todo esto, he decido presentarles algunos consejos para mantener nuestras aplicaciones con un nivel seguridad medio ya que para que un sistema, servicio web o página este completamente segura depende de varios roles de seguridad que van desde la seguridad de infraestructura y red hasta la programación y presentación de la información.

Estos consejos de seguridad van directamente a programadores PHP ya que es el lenguaje de programación al cual me he orientado y el cual conozco mejor que los demás.

1. Validar Formularios

Es sumamente importante validar todos y cada uno de los inputs que recibimos por medio de un formulario web. No solo si el input está vacío, sino también, validar el tipo de dato. Por ejemplo, validar que un campo de email tenga el formato especifico validando el carácter “@” y que concluya con .ext. De igual forma, si pedimos edad o un numero validar que el input sea un entero, etc.

Esta validación la podemos hacer tanto con php a nivel de validación por eventos con php o utilizando Javascript de igual forma.

Otro punto es validad la información que viene de un formulario a partir de una sesión determinada para evitar recibir doble posting de datos o simplemente evitar que alguien se le ocurra darle varias veces al botos de enviar. Una solución que yo recomiendo a mis estudiantes y clientes es utilizar recapcha, propia o de un tercero como recapcha de google.

Otra forma sería utilizando tokens, por ejemplo:

<?php
//preguntamos si el token enviado es igual al que tenemos en sesión
if ($_SESSION['token'] == $_POST['token']) {

//procesamos formulario

} else {
//enviamos un mensaje de alerta o simplemente direccionamiento
}
//creamos la variable token con calor encriptado
$token = md5(uniqid(rand(), true));
//creamos una variable de session con el valor generado
$_SESSION['token'] = $token;
?>
<form id="myFrom" action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="post">
<div><input type="hidden" name="token" value="<?php echo $token; ?>" />
<input type="text" name="myText"
value="<?php echo(isset($_POST['myText']) ? $_POST['myText'] : ''); ?>" />
<input type="submit" value="Save" name="submit" /></div>
</form>

Como vemos el truco es enviar el valor como valor del array _POST para ser comparado como validación para el procesamiento de formulario. Sencillo verdad!

2. Proteger la base de datos

Cuando hablamos de proteger la base de datos, no me refiero a el servidor de base de datos ni mucho menos a los roles de usuarios. Siempre hablo en sentido de programación, salvo mencione lo contrario. Digo esto por que, si bien he visto en practica que algunos cuando estamos realizando consultas en las bases de datos lo hacemos directamente insertando el valor que viene por un campo de un formulario de búsqueda o de registro de información y no procesamos ese valor antes de utilizarlos en la consulta o query, sino que se utiliza de manera directa.

Por ejemplo:

$select = "SELECT * FROM news WHERE newsid = '". $_POST['id']."'";

Esto es una mala utilización de las variables dentro de las consultas. Lo indicado es procesar la variable antes de utilizarla en le query como les había dicho. Una recomendación sencilla seria esto:

//limpiamos la variable captada mediante _POST

$var1= mysql_real_escape_string($_POST['id']);

//Ahora utilizamos la variable limpia

$select = "SELECT * FROM news WHERE newsid = '".$var1."'";

Para manejo de variables con valores string también debemos tomar en cuanta addslashes. Véase http://www.php.net/manual/en/security.php para ampliar sobre estas técnicas.

3. Proteger la información de sesiones y cookies

Cuando trabajamos con aplicaciones y paginas que requieren algún tipo de autenticidad como sistemas de foros, áreas exclusivas, etc. es necesario tener los valores de sesión o de cookie para mantener al usuario validado constantemente mientras navegas por las áreas restringidas. El uso de de sesiones y cookies es lo mas común por ahora, donde almacenamos los datos como usuario, clave, no. de cuenta etc. cosa de permitirnos no solo autenticar al usuario constantemente y mantenerlo en acceso, sino también para ahorrarnos hacer querys o consultas sencillas cuyos resultados podemos manejar de otra forma y ahorrar tiempo, código y recursos del servidor.

Ojo con eso. He visitado sitios donde una vez creada una cuenta como miembro, proceso a verificar el archivo de sesión físico, y me encuentro con que esta registrada en sesión mi nombre de usuario, mi clave – sin encriptar, mi record completo incluyendo mi correo electrónico, edad, etc.

Esto no debe hacerse de ninguna forma, las sesiones o cookies deben ser unicamente para validar y almacenar variables globales que son necesarias en nuestros sistemas sin que pongan en riesgo la información del usuario.

Recomiendo encriptar toda la información que se pueda y nunca, pero nunca almacener el registro completo o perfil de un usuario. Otra alternativa es almacenar la sesión en la base de datos y por supuesto, refiéranse a la función session_set_save_handler() de php para mejorar aun mas la seguridad y control.

4. Proteger nuestras aplicaciones de las vulnerabilidades XSS (Cross-site scripting)

Cuando hablamos de XSS me refiero a la vulnerabilidad de nuestros sitios de permitir la inserción de código html o sql en nuestras paginas. Por ejemplo, en un sistemas que permita comentarios y que no filtre los tags html podría ocasionar que alguien puede insertar un javascript que habrá un pop up hacia su sitio en el mejor de los casos.

Para evitar esto debemos retomar la sugerencia No. 1 de este articulo y controlar los caracteres especiales que permitimos en nuestros formularios.

Una forma sencilla pero eficaz es utilizar la función php htmlentities() antes de imprimir o presentar un valor adquirido por formulario. Por ejemplo:

En vez de esto:

echo $_POST['myText'];

Hacemos esto:

echo htmlentities($_POST['myText']);

5. Proteger nuestros formularios de códigos inválidos

De nuevo, hacemos referencia a la sugerencia No. 1 de este articulo, y nos vamos aun mas allá. En ocasiones no es suficiente con validar nuestros campos en los formularios, sino que, también es recomendable evitar esos caracteres especiales que a veces se filtran haciendo copy/paste, desde ms word por ejemplo. Los cuales causan un total desorden en cuanto a texto plano se refiere.

Esto debe evitarse a toda costa. El ejemplo mas común es el de comillas simple o ” ‘ “, la cual aparece casi siempre en algún texto en ingles u otro idioma que emplea estos caracteres. Otro caso es de las diéresis, etc. Lo que debemos convertir estos caracteres en código html:

Para evitar, la comillas simple utilizamos la función php addslashes() para evitar que nuestro sistemas presenten error con estos caracteres:

<?php
$str = "Is your name O'reilly?";

// Outputs: Is your name O\'reilly?
echo addslashes($str);
?>

6. Protección contra ataques CSRF (Cross-Site Request Forgeries)

Cuando hablamos de CSRF, hablamos de la complicidad de nuestros usuarios para con los ataques de sujetos caprichosos y simplemente curiosos. Se presenta cuando pasamos valores mediante url para luego ser utilizados como fuente de referencia para consultas de sql, valor de funciones, etc.

Si bien un usuario puede utilizar una sesión para ser validado y navega nuestro sitio, luego se desconecta o cierra la sesión, aun así el histórico del navegador almacena los url que hemos visitado. Para evitar que sea desplegada información critica debemos asegurar que nuestro sistema siempre valide la sesión antes de presentar la información.

También, evitar la utilización de valores de array _REQUEST para información critica. Ejemplo simple:

En vez de esto:

<?php
if ($_REQUEST['submit'] == 'Save') {
echo("<p>I am processing your text: ");
echo(htmlentities($_REQUEST['text']));
echo("</p>");
}
?>

Hacemos esto:

<?php
if ($_POST['submit'] == 'Save') {
echo("<p>I am processing your text: ");
echo(htmlentities($_POST['text']));
echo("</p>");
}
?>

Véase la llamada a la sugerencia No. 4 al desplegar el valor de la variable.

7. Proteger nuestra estructura de archivos

Al referirme a proteger el sistema de archivos, me refiero a la estructura de las carpetas de nuestro servidor web a la hora de diseñar la estructura física de nuestros sitios web y/o aplicaciones.

Es importante que las carpetas que contienen archivos de imágenes y que no deseamos que sean visualizados fuera de nuestro entorno o de nuestra pagina o aplicación sean ocultadas mediante permisos del servidor o bien mediante htaccess.

Cuando no tenemos control del servidor, ni menos de poder utilizar ficheros .htaccess un simple archivo index.html, index.htm, default.html, o cualquiera que sea nuestro directory index ayuda considerablemente la seguridad de los archivos de forma sencilla. Es muy eficaz para carpetas que contienen archivos de tipo include como vistas, paneles, funciones, librerias, etc.

8. Prevenir acceso a archivos de inclusión

Hoy día es muy popular la utilización de AJAX para mejorar la experiencia del usuario, pero cuidado, esto requiere la utilizacion de muchos archivos includes y de remote call en paneles y data grids. Es importante que estos archivos de inclusión no sean visto directamente. Ejemplo de esto es, si tenemos un archivo llamado main.php otro profile.php y otro extra.php los cuales son incluidos a llamados por index.php entonces debemos aseguras los archivos a ser llamados por index.php para que solo este o a través de este pueda verse su contenido.

Para lograr esto hacemos lo siguiente:

En nuestro archivo index.php agregamos la siguiente linea de código:

//declaramos una constante para controlar quien puede o no ver los includes
<?php define('APPLICATION', true); ?>

En los archivos main.php, profile.php y extra.php que son los archivos a incluir agregamos esta linea:

<?php if(!defined('APPLICATION')) exit; ?>

Así de simple podemos asegurar todos archivos de inclusión en nuestras aplicaciones web.

Esta guía es una introducción o guía básica de seguridad para programadores PHP. Para mas información y consejos avanzados refiéranse a http://www.php.net/manual/en/security.php.

Fuente de referencia: http://www.ibm.com/developerworks/opensource/library/os-php-secure-apps/index.html