Ver Mensaje Individual
  #2 (permalink)  
Antiguo 06/09/2012, 05:42
Avatar de Triby
Triby
Mod on free time
 
Fecha de Ingreso: agosto-2008
Ubicación: $MX->Gto['León'];
Mensajes: 10.106
Antigüedad: 15 años, 1 mes
Puntos: 2237
Respuesta: [Aporte] Seguridad básica en PHP

Cuarta Parte -> Subiendo archivos

Hace algún tiempo, un usuario creó un tema para debatir sobre los riesgos que hay al subir imágenes al servidor y la posibilidad de ejecutar código PHP malicioso por ese medio: http://www.forosdelweb.com/f18/ataqu...agenes-964823/

Pasaremos este tema por alto, ya que la solución es sencilla y la vimos en la primera parte: "Jamás incluir un archivo sin saber de qué se trata"; aparte, si validamos correctamente la subida, no nos tenemos que preocupar por estas cosas raras.

Lo primero que tenemos que hacer al subir archivos a nuestro servidor es asegurarnos de que se trata de una extensión permitida, después, de ser posible, verificamos que el contenido corresponda a esa extensión y, finalmente, nunca lo vamos a usar en una sentencia include o require.

Por supuesto, puede haber proyectos en los que tengamos que subir archivos o scripts ejecutables (php, js, html, etc.) y nunca debemos dejarlos accesibles desde URL, forzándolos para descarga cuando sean solicitados.

Cita:
Iniciado por Sugerido por Abimaelrc
Usando la librería Fileinfo podemos obtener más información de los archivos y, en base a eso, determinar las acciones a realizar.
Código PHP:
Ver original
  1. // Si vamos a subir imágenes, tenemos que saber cuáles permitir
  2. $extensiones = array('jpg', 'jpeg', 'gif', 'png', 'bmp');
  3.  
  4. // Verificamos si existe el campo de archivo de nuestro formulario
  5. if( ! isset($_FILES['imagen'])) {
  6.     // Ups!, no hay archivo
  7.     die('No modifiques mi formulario, por favor.');
  8. }
  9.  
  10. // Ahora revisamos que realmente se subió un archivo
  11. if($_FILES['imagen']['error'] == 4) {
  12.     // El error 4 corresponde a campo de archivo vacío
  13.     die('Selecciona un archivo de tu PC para subir.');
  14. }
  15. if($_FILES['imagen']['error'] == 0) {
  16.     // El error 0 quiere decir que se subió el archivo correctamente
  17.     // Obtenemos la extensión, en minúsculas para poder comparar
  18.     // Si no sabes que hacen strtolower(), end() o alguna otra función... consulta el manual de PHP
  19.     $extension = strtolower(end(explode('.', $_FILE['imagen']['name'])));
  20.  
  21.     // verificamos que sea una extensión permitida
  22.     if( ! in_array($extension, $extensiones)) {
  23.         die('Sólo se permite subir archivos con estas extensiones: ' . implode(', ', $extensiones);
  24.     }
  25.  
  26.     // Ahora, con la librería GD verificamos la imagen (o podrías usar imagick)
  27.     if($extension == 'jpg' || $extension == 'jpeg') {
  28.         $imagen = createimagefromjpeg($_FILE['imagen']['tmp_name']);
  29.     } else if($extension == 'gif') {
  30.         $imagen = createimagefromgif($_FILE['imagen']['tmp_name']);
  31.     }
  32.     // Haz lo mismo para png, bmp o cualquier otro archivo de imagen
  33.     // Consulta el manual de PHP -> GD para saber más
  34.  
  35.     // Ahora verificamos que el archivo sea realmente una imagen
  36.     $datos = getimagesize($imagen);
  37.  
  38.     // Si la función falla, devolverá falso y, aunque podría ser una imagen real que no se reconoció
  39.     // correctamente, mejor consideramos que se trata de un intento de ataque :borracho:
  40.     if($datos === false) {
  41.         // Nótese el triple signo igual, para comparar valor y tipo
  42.         die('No se pudo subir la imagen, intenta con otro formato.');
  43.     }
  44.  
  45.     // Ok, aquí movemos la imagen a su destino final con move_uploaded_file()
  46.     // Y, si corresponde, actualizamos base de datos
  47.     // Ojo, hay que revisar si se va a sobreescribir un archivo
  48.     // y crear la lógica para renombrar en caso de ser necesario
  49. } else {
  50.     // Más info sobre códigos de error subiendo
  51.     // archivos en el manual de PHP
  52.     die('Hubo un error subiendo la imagen.');
  53. }

Como mencionaba, si vamos a subir archivos que puedan ejecutarse, los guardamos en una ruta no disponible desde URL y forzamos a que sean descargados: http://www.forosdelweb.com/f18/forza...2/#post2303479

Quinta Parte -> Acceso a datos importantes

Hace algunos ayeres, se tenía la costumbre de guardar la configuración del sitio en archivos con extensiones diferentes de php, tal vez con la finalidad de identificarlos fácilmente, pero esa práctica es un error, ya que los archivos con extensiones no registradas como aplicación podrían mostrarse como archivos de texto o quedar disponibles para descarga.

Entre otras extensiones, solía usarse: inc, dat, cfg, txt, etc.; a PHP no le importa la extensión del archivo, si tiene código PHP válido, lo ejecutará.

De hecho, todavía existen aplicaciones prefabricadas que incurren en esta mala práctica y por ahora no recuerdo alguna.

Supongamos que tenemos un script para guardar la configuración del sitio, llamado config.inc

Código PHP:
Ver original
  1. <?php
  2. $dbhost = 'localhost';
  3. $dbuser = 'usuario';
  4. $dbpass = 'contraseña';
  5.  
  6. $db = mysqli_connect($dbhost, $dbuser, $dbpass);
  7.  
  8. // Fin de archivo

Si un usuario malintencionado coloca en la barra de direcciones del navegador sitio.com/config.inc, obtendrá información que le dará acceso a la base de datos.

Cómo soluciono este problema?
- Registra la extensión inc como ejecutable de PHP (no recomendado)
- Modifica tu .htaccess para evitar que los archivos .inc sean accesibles desde URL (no recomendado)
- NUNCA uses otras extensiones para tus scripts PHP!!!
- En tu servidor de producción debes desactivar todos los mensajes de error, ya que estos podrían revelar información de estos archivos. Si siempre usas la extensión php, codificas correctamente y validas todas tus variables, nunca verás mensajes de error, excepto cuando haya problemas en el servidor.

Otra medida que podemos tomar es evitar acceso por URL a scripts que están pensados exclusivamente para ser incluidos, como el del ejemplo anterior o uno donde tenemos funciones comunes para todo el sitio, etc.

Sólo tenemos que definir una constante (de preferencia) o una variable para saber si el ingreso a los scripts es válido

Código PHP:
Ver original
  1. <?php
  2. // index.php - Nuestra página principal
  3.  
  4. // Establecemos la constante
  5. define('MI_PROYECTO', 1);
  6.  
  7. // Cargar info y establecer conexión a base de datos
  8. include 'config.php';

Código PHP:
Ver original
  1. <?php
  2. // config.php y cualquier otro script que no deba accederse por URL
  3.  
  4. if( ! defined('MI_PROYECTO')) {
  5.     die('No tienes acceso a esta carpeta o archivo.');
  6. }

Igualmente, debes evitar que se liste el contenido de tus carpetas, ya sea creando un index.php vacío en cada carpeta o modificando el .htaccess en la raíz del sitio agregando la siguiente línea:

Código APACHE:
Ver original
  1. Options -Indexes

Cita:
Iniciado por Sugerido por Abimaelrc
También es recomendable que archivos importantes estén en una ruta no accesible desde web, por ejemplo:

misitio.com está hospedado en un sistema linux donde la carpeta de usuario es /home/misitio y la carpeta pública (web) es /home/misitio/public_html, entonces, podemos crear una carpeta en el mismo nivel de public_html para almacenar estos archivos, que serían accesibles por script (PHP) pero no directamente desde un navegador, ejemplo: /home/misitio/config

Sexta Parte -> Funciones peligrosas

Directivas include, include_once, require y require_once - Se usan para incluir e interpretar scripts PHP.

Por si todavía no ha quedado claro, nunca se deben incluir archivos que no sabemos su procedencia o mediante variables que no fueron verificadas apropiadamente; así mismo, debemos evitar incluir archivos de otros sitios (include 'http://www.ejemplo.com/script.php';), a menos que sean de fuentes confiables.

eval() - El manual de PHP para esta función hace la siguiente advertencia:

Cita:
Precaución

El constructor de lenguaje eval() es muy peligroso porque permite la ejecución de código de PHP arbitrario. Su uso está totalmente desaconsejado. Si se ha verificado cuidadosamente que no existe otra opción que usar este constructor, se ha de poner especial atención en no pasar ninguna información proporcionada por el usuario a esta función sin haberla validado apropiadamente con anterioridad.

Enlace: http://www.php.net/eval

exec(), system() y shell_exec() - Ejecutar comandos.

Básicamente, shell_exec() permite ejecutar comandos directamente relacionados con el sistema operativo, como obtener listado de archivos y carpetas, mientras que exec() y system() permiten ejecutar cualquier comando.

Más info en: http://www.php.net/exec

No voy a abarcar mucho sobre este tema porque es muy extenso, me voy a limitar a decir que no se deben usar a menos que sea estrictamente necesario y sepas lo que haces. En mi caso, sólo he usado exec() porque debo convertir videos a diferentes formatos mediante ffmpeg y nunca se debe incluir información manipulable por el usuario.

Seguramente hay otras directivas y funciones que comprometen la seguridad del sitio o del servidor mismo al ser usadas sin buena planeación, agrega un comentario si sabes (o tienes duda) de alguna.

-- Continuará --
__________________
- León, Guanajuato
- GV-Foto

Última edición por Triby; 13/12/2012 a las 17:29