Comunidad de diseño web y desarrollo en internet

Forzar descargas con PHP

Hacer descargables cualquier tipo de archivos no es muy complicado. La mayoría de las tecnologías del lado del servidor nos permiten hacer esto, en este caso veremos cómo es con PHP.

El script fue sacado del todopoderoso php.net y es el siguiente:

<?php
    $f = $_GET["f"];
    header("Content-type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$f\"\n");
    $fp=fopen("$f", "r");
    fpassthru($fp);
?>

De esta forma, cuando necesitemos que el explorador quiera descargar, por ejemplo un archivo de imagen, lo llamaríamos  descargar.php?f=imagen.jpg.

Y el navegador abrirá el diálogo de descarga:

Forzar Descargas con PHP

Sencillo, no? Bien, pero podría haber serios problemas, dejar el script así podría hacer descargable cualquier tipo de archivo, incluyendo el código fuente de otros archivos php.

A continuación veremos cómo implementar algunas medidas de seguridad.

Haciendo el script más seguro

Aquí podemos tener dos opciones, enlistar los tipos de archivos que pueden ser descargables o listar las extensiones de los archivos que podemos descargar.

Además, para evitar que se pueda navegar en otros directorios, vamos a implementar una comprobación.

Listando los archivos descargables.

Útil cuando son relativamente pocos los archivos descargables, los nombres los metemos en un arreglo y hacemos una sencilla comprobación y listo.

<?php
    $archivos = array("imagen.jpg", "logo.png", "flash.swf");
    $f = $_GET["f"];
    if(strpos($f,"/")!==false){
        die("No puedes navegar por otros directorios");
    }
    if(!in_array($f,$archivos)){
        die("<b>ERROR!</b> no es posible descargar $f");
    }
    header("Content-type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$f\"\n");
    $fp=fopen("$f", "r");
    fpassthru($fp);
?>

De esta forma, si queremos descargar un archivo que no sean los que enlistamos en el arreglo, nos aparecerá el mensaje de error:

ERROR! no es posible descargar archivo.ext

Además este script es sensitivo a mayúsculas y minúsculas, no es lo mismo flash.swf que Flash.swf, fLaSh.SwF... etc

Pero qué pasa cuando tenemos 100 imagenes en formato jpg, png y gif que querramos que el usuario pueda descagar? Para eso usamos un filtrado de extensión.

Listando las extensiones de los archivos que puedan ser descargables

La lógica es igual, usar in_array para saber si el archivo a descargar tiene una de las extensiones que hemos marcado como descargables, esto se hace con explode y count, seguramente habrá una forma más eficiente de hacerlo pero no la conozco.

<?php
    $extensiones = array("jpg", "jpeg", "png", "gif");
    $f = $_GET["f"];
    if(strpos($f,"/")!==false){
        die("No puedes navegar por otros directorios");
    }
    $ftmp = explode(".",$f);
    $fExt = strtolower($ftmp[count($ftmp)-1]);

    if(!in_array($fExt,$extensiones)){
        die("<b>ERROR!</b> no es posible descargar archivos con la extensión $fExt");
    }

    header("Content-type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$f\"\n");
    $fp=fopen("$f", "r");
    fpassthru($fp);
?> 

Y con esto la descarga de archivos se hace más fácil y más segura. Espero les haya servido.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate