Recientemente tuve la necesidad de escribir un Script en PHP que ejecutara comandos en el sistema. La manera rapida de hacerlo, es a traves de la funcion shell_exec que PHP provee. Sin embargo, siempre hay que recorder una regla de oro de programacion.
NO CONFIES EN EL USUARIO
Especialmente cuando los argumentos de la funcion, son introducidos por un usuario, y eso nos deriva a otras reglas de programacion segura, por ejemplo, siempre hay que "limpiar" la informacion que viene del usuario. En otras palabras, siempre hay que verificar que la informacion que los usuarios nos proveen, sea exactamente la que estamos esperando, especialmente si esta información será utilizada para ejecutar comandos del sistema.
La configuracion de Apache (o cualquier otro web deamon), PHP.ini o del sistema operativo quedan fuera del ambito de este articulo, ya que en la mayoria de las ocasiones, los servidores webs tienen la funcion shell_exec deshabilitada. En cambio, este artículo se centra especificamente en la sanitización y validación de la información con PHP, utilizando expresiones regulares.
Hace poco, necesitaba escribir un programa que recibiera dos parametros del usuario. Una IP, y un número entero. El script necesitaba asociar el IP con una dirección MAC, y utilizar el numero como un "estado" ya sea de exito de error.
Primero que nada, tenemos que validar si el script se esta llamando correctamente, por lo utilizo las siguientes instrucciones:
<?php isset($_GET['ip']) or die ("La direccion IP no esta definida"); isset($_GET['status']) or die ("El resultado no esta definido"); ?>
isset simplemente determina si una variable se encuentra asignada,
or, es un operador unitario que ejecuta el comando de la derecha (die), si el comando a la izquierda (isset) falla.
die, es una funcion que detiene la ejecucion del script.
Basicamente, si los argumentos no son pasados a la funcion, el programa termina su ejecución.
Ahora viene la parte en la que tenemos que validar la información que estamos recibiendo. En otras palabras, asegurarnos de que lo estamos recibiendo es una IP y un numero que solamente puede ser 1 ó 0.
Una excelente herramienta para validar "Strings" son las expresiones regulares, pero...
Qué son las expresiones regulares?
Sencillo! En palabras mortales, es una manera de decirle al interprete o compilador, que la información debe cumplir con cierto patrón, y tiene estados definidos de aceptación. Como lo veremos a continuación.
Una IP es un conjunto de 12 números, divididos en 4 grupos de 3 digitos, que oscilan entre 0-255. Por lo que una IP con el formato 500.604.234.123 no es un número IP válido. La expresión regular para validar una IP es la siguiente:
^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
y en PHP utilizamos la función preg_match para evaluar expresiones regulares, y se utiliza de la siguiente manera:
<?php $sanitize_input = "/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/"; $IP = (preg_match($sanitize_input, $_GET['ip']))?$_GET['ip']:NULL; ?>
Las expresiones regulares en PHP siempre se escriben asi:
/expresion-regular/
Por lo tanto, todo lo que se encuentra entre diagonales, es parte de la expresion regular. Ahora procederemos a explicarla:
- ^ significa que la string a comparar tiene que empezar con el caracter que le sigue.
- () define grupos de caracters
- [ - ] define secuencias
- ? compara el caracter de la string original, contra cualquiera que este a la derecha de este operador. Da la alternativa, que si ese caracter esta o no presente, la expresion regular sigue siendo valida.
- {} define ciclos, o iteraciones
- . acepta como válido, el caracter que le sigue, pero solamente ese.
- $ La string a validar, debe terminar con el caracter que se encuentra a la izquierda del operador.
- | es el operador or. Define alternativas, o grupos de patrones a validar.
Ya explicada la función de cada operador en la expresión regular, procederemos a verla en conjunto.
Lo primero que viene, es el operador ^ que forza al interprete a que la información que reciba debe ser un número, ya que inmediatamente despues del operador ^ viene definida una secuencia de caracteres, que esta conformada solamente por números.
La porción de la expresión regular delimitada por [1-9]?[0-9] Le dice al interprete, que cualquier número del 1 al 9 es válido. Esta porción espera cualquier número entre 1 y 99, sin embargo, si el primer bloque de la IP es un número entre 0-9, tambien lo acepta como valido, dado que el operador ? le dice al intérprete, que es correcto omitir el primer caracter. Le sigue el operador or que en esta expresión regular tiene la función de validar las IP por rangos. Es decir, solamente vamos a aceptar números en el rango del 1-99, 100-199, 200-249, 250 al 255.
El siguiente bloque en el operador or esta definido como 1[0-9]{2}. De esta forma le informamos al interprete, que la IP, puede empezar con un 1, seguido de dos números que van del 0 al 9.
2[0-4][0-9] Es muy similar a la primera condición, solo le informa al interprete que la IP también puede comenzar con un 2, seguído de dos números. El primer número debe estar entre 0 y 4, y el siguiente número debe estar en 0 y 9. De esta manera, definimos el rango de 200 a 249.
Finalmente definimos el ultimo rango en el que una IP puede ser válida. 25[0-5] a esta altura del artículo, ya debe ser pan comido entender la expresión regular. Simplemente le informamos al interprete, que de acuerdo a la expresión regular, la IP debe iniciar con 25 y cualquier número entre 0 y 5, dando como máximo valor posible 255.
Finalmente, cerramos el grupo y le informamos al interprete que ese patrón debe repetirse 3 veces {3} copiamos la misma expresión regular y la "sellamos" con un símbolo de $ que le dice al interprete que el último caracter en la expresión regular, debe ser un número. De esta manera evitamos input como 127.0.0.1'-- or 1 = 1 para intentar inyectar codigo SQL o correr funciones del sistema.
Ahora que la información ha sido validad, podemos proceder a utilizarla como ha sido destinada.
Espero que les haya sido de ayuda, si el artículo necesita ser mas claro o especifico, haganlo a través de los comentarios!