php - force - Restricción de descargas de archivos
php header download file (8)
Básicamente no le das a los usuarios la URL directa al archivo. Los permisos basados en servidor no tienen nada que hacer aquí.
Digamos que tiene los archivos necesarios guardados en /data/files/file.pdf (una buena práctica para almacenar archivos desde su raíz web). Puede proporcionar a los usuarios un enlace para descargar que se parece a /download.php?auth=32
Cuando un usuario hace clic en el enlace, download.php verificará si la sesión / cookie está autenticada y si el id de descarga es válido (en caso de que tenga un vencimiento de descarga basado en el tiempo) Then download.php leerá el archivo requerido desde su ubicación y lo enviará al navegador con encabezados apropiados para forzar la descarga.
Actualmente estoy creando un sitio web para un cliente que básicamente implica vender varios archivos. Obviamente, esto es algo muy común de hacer, lo que me hace sentir como un tonto por no pensar en un método para hacerlo.
Una vez realizada la compra, se debe llevar al cliente a una página que contiene el enlace de descarga, así como a recibir correos electrónicos que contienen un enlace de descarga y un correo electrónico con información sobre una cuenta que se creará para ellos (también podrán descargar desde el panel de control de su cuenta). Lo que trato de averiguar es cómo puedo ocultar / ocultar la ubicación del archivo en mi servidor para que una persona que lo compre no pueda simplemente copiar y pegar el enlace directo al archivo en otro lugar. Incluso si solicito descargar un archivo, un enlace del formato http://example.com/blah/download/454643 , una URL que no corresponde a la ubicación real del archivo, creo que aún podría ser posible. para ubicar el archivo en el servidor? Realmente no entiendo demasiado sobre cómo funcionan los permisos en mi servidor, por lo que pregunto. Gracias por adelantado :)
Bueno, primero, definitivamente no desea vincular directamente al archivo. Probablemente desee enviar al usuario un enlace a un servicio que haya creado (o incluso solo una página) con un argumento id generado que resulte en una descarga de archivos, si se cumplen ciertos criterios.
Esos criterios son difíciles, en realidad, ya que necesita permitir que el usuario descargue el archivo más de una vez (en caso de que no pueda descargar el archivo completo la primera vez, o lo borre accidentalmente, etc.), pero una vez que el enlace funciona, funciona hasta que lo mates.
Sugeriría usar tiempo o IP para filtrar sus solicitudes de descarga.
Tiempo: cuando alguien le compre el archivo, infórmeles que podrán descargar el archivo solo por 1 día, o algo así. Sí, otras personas pueden descargar el archivo durante ese día, pero solo por 1 día. También podría establecer un límite de descarga para que solo puedan descargarlo 5 veces (esto es normal).
IP: cuando alguien le compre el archivo, infórmeles que solo podrán descargar el archivo de esa IP. Su servicio de descarga ciertamente puede verificar esto cuando intenten descargar el archivo.
Parece que ambos son fáciles de usar al mismo tiempo, también.
En ambos casos (o ambos), prepárese para manejar a los clientes que no descargaron el archivo a tiempo, o si desean obtenerlo nuevamente después del límite de tiempo (o desde otra computadora / IP (algunas personas no reciben los estáticos) ) No querrán volver a pagar, y probablemente no deberían tener que hacerlo.
Puede hacer que la URL sea un código de autorización para el comprador. Haz que inicie sesión de nuevo, comprueba para qué archivo está el código y luego envíale el archivo. Aquí hay un ejemplo de código PHP de osCommerce (lo escribí hace mucho tiempo).
// Now send the file with header() magic
header("Expires: Mon, 26 Nov 1962 00:00:00 GMT");
header("Last-Modified: " . gmdate("D,d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: Application/octet-stream");
header("Content-disposition: attachment; filename=" . $downloads[''orders_products_filename'']);
if (DOWNLOAD_BY_REDIRECT == ''true'') {
// This will work only on Unix/Linux hosts
tep_unlink_temp_dir(DIR_FS_DOWNLOAD_PUBLIC);
$tempdir = tep_random_name();
umask(0000);
mkdir(DIR_FS_DOWNLOAD_PUBLIC . $tempdir, 0777);
symlink(DIR_FS_DOWNLOAD . $downloads[''orders_products_filename''], DIR_FS_DOWNLOAD_PUBLIC . $tempdir . ''/'' . $downloads[''orders_products_filename'']);
if (file_exists(DIR_FS_DOWNLOAD_PUBLIC . $tempdir . ''/'' . $downloads[''orders_products_filename''])) {
tep_redirect(tep_href_link(DIR_WS_DOWNLOAD_PUBLIC . $tempdir . ''/'' . $downloads[''orders_products_filename'']));
}
}
Una gran cantidad de URL de descarga que he visto y que se basan en la compra tienden a usar algún guid y otra información dinámica como parte de la URL para que no sea tan simple como adivinar una identificación. Podría terminar con guid / datetimepurchased / id o algo así como parte de la ruta.
Una opción adicional sería asegurarse de que el usuario haya iniciado sesión antes de permitir que continúe la descarga, lo que daría una capa adicional de seguridad.
Almacene los archivos fuera de su raíz web, pero luego asegúrese de que la carpeta donde los almacena esté en la directiva "open_basedir" en su archivo php.ini, esto le permitirá acceder a ellos desde un script PHP. Almacenarlos fuera de la raíz web significa que nunca se accederá directamente a ellos a través de HTTP.
Tenga un script PHP, como los que figuran en estas respuestas que pueden transmitir / leer un archivo. Si es un archivo grande, es posible que deba cambiar el "max_execution_time" para tener en cuenta el tiempo extra que tomará el script para leer el archivo. Este script le permitirá autenticar al usuario y verificar que haya pagado el archivo.
Coloque un .htacces en la carpeta con la secuencia de comandos que reescribe el archivo solicitado de esa carpeta a una variable. Eso hace que parezca que están accediendo directamente al archivo mientras que no lo están. Personalmente, solo pondría el script individual en esta carpeta solo para mantener las cosas simples. Asi que:
en realidad vuelve a escribir a:
http://www.yourdomain.com/files/download_file.php?filename=expensive_song.mp3
Buena suerte.
Aquí hay un código de muestra de lo que he hecho para algo bastante similar:
// $mimeType is the mime type of the file
header(''Content-type: '' . $mimeType);
// this will get the size of the file
// (helps for giving the size to the browser so a percent complete can be shown)
header(''Content-length: '' . (string) (filesize($path)));
// disposition is either attachment (for binary files that can''t be read by the browser)
// or inline (for files that can be read by the browser
// some times you have play with this to get working so users get the download window in all browsers
// original filename is the name you want to users to see
// (shouldn''t have any special characters as you can end up with weird issues)
header(''Content-Disposition: '' . $disposition . ''; filename='' . $originalFilename);
// the next 2 lines try to help the browser understand that the file can''t be cached
// and should be downloaded (not viewed)
header(''Pragma: Public'');
header(''Cache-control: private'');
// this will output the file to browser
readfile($path);
Por supuesto, puede agregar a esto cualquier comprobación de inicio de sesión y registro para asegurarse de que no se descargue muchas veces.
Además, como se dijo anteriormente, asegúrese de colocar el archivo fuera (o arriba) de la raíz del documento del servidor web para que las personas no puedan encontrar la ruta. O incluso podría poner una contraseña en el directorio para que solo las personas internas puedan acceder a la lista de archivos más fácilmente, pero no lo recomendaría. (Solo haz esto si puedes poner algo fuera de la raíz del documento).
Si tiene acceso a ejecutar Lighttpd, definitivamente debería verificar el módulo Mod_SecDownload . Lo he usado en un proyecto anterior que vendía archivos de video e imágenes de forma segura.
Como el servidor web no sabe nada sobre los permisos usados en la aplicación, la URL resultante estaría disponible para cada usuario que conozca la URL.
mod_secdownload elimina este problema al introducir una forma de autenticar una URL por un tiempo específico. La aplicación debe generar un token y una marca de tiempo que el servidor web verifica antes de permitir que el servidor web descargue el archivo.
La URL generada debe tener el formato:
<uri-prefix> / <token> / <timestamp-in-hex> / <rel-path> que se parece a "yourserver.com/bf32df9cdb54894b22e09d0ed87326fc/435cc8cc/secure.tar.gz"
<token> es un MD5 de
- una cadena secreta (proporcionada por el usuario)
- (comienza con /)
- <timestamp-in-hex>
Como puede ver, el token no está ligado al usuario en absoluto. El único factor limitante es la marca de tiempo que se utiliza para invalidar la URL después de un tiempo de espera dado (secdownload.timeout).
Algunos servidores web, como Lighty y Nginx, implementan el encabezado X-Sendfile. Supongamos que tiene una aplicación Django, puede hacer que su vista devuelva un encabezado X-Sendfile que apunta al archivo que desea enviar. lighttpd luego servirá ese archivo en su lugar.
El archivo puede estar en una ubicación no accesible en la web (esto no es un redireccionamiento 301) y dado que su aplicación está publicando el encabezado, primero puede hacer la autorización.
Esto es mucho mejor que servir archivos estáticos de su aplicación. El servidor web está optimizado para archivos estáticos y será más rápido y mucho más liviano en recursos. Si está manejando más de unas pocas solicitudes, debería considerar usar X-Sendfile.
Hay una buena publicación en el blog aquí:
http://blog.zacharyvoase.com/2009/09/08/sendfile/
Las instrucciones Lighttpd / PHP se pueden encontrar aquí:
http://redmine.lighttpd.net/wiki/1/X-LIGHTTPD-send-file
Las instrucciones de NGINX se pueden encontrar aquí:
http://wiki.nginx.org/XSendfile
También parece haber una versión Apache de lanzamiento anticipado que hace lo mismo: