mysqli – How to generate and protect file url?

Question:

How can I hide the original urls of the file? and at the same time generate unique urls for a single use or for a single visit example:

From:

example.com/archivo/video.mp4

A:

example.com/files/videos/premium/782d1a1a7/a0a34c39995e1fa95efdbf5.mp4

Unique urls

That is, if the user has already visited the following Urls

example.com/files/videos/premium/782d1a1a7/a0a34c39995e1fa95efdbf5.mp4

Show an error message

The requested link has expired.

The Urls already visited is no longer available and at the same time generating a new Url in the database in order to prevent the link or links from being shared.

The address of the Urls will be through the database, and thus when visiting the Url of the file create an Update of the new url.

My idea table archive.sql

id          url_generate
1      a0a34c39995e1fa95efdbf5

PHP Url generator

function generateRandomString($length = 100) { 
    return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length); 
}

?>

<div><?php echo generateRandomString(); ?></div>

Can you explain to me how to create the php code?

A simple example of how it would work.

Answer:

First, extracting the basic idea, the system has somewhere a file that you want to give selective access through a unique identifier that expires when used.

Second the identifier is part of the Url.

For the first part you need:

1) The file must be in a non-public directory so that it is not even possible to guess the actual url. But php must be able to read that directory in order to download the file.

For example if the site is being served from /home/public_html , the files to download would be stored in /home/archivos_privados .

2) The system to give access must generate a random string, associate it to the physical file and save that association somewhere until it is used. It can be in the session or in a table in the database. For the purposes of this question, a table would be used in the database that allows the link to persist even after the session has ended.

Example: For the file /home/archivos_privados/un_archivo.pdf , a string 'XcV34kP56tF' is generated and they are saved in the table tabla_asociaciones in the database.

The table tabla_asociaciones must have at tabla_asociaciones two columns:
nombre_archivo_real
codigo_aleatorio

To insert the data:

$nombre_archivo = 'un_archivo.pdf';  
$codigo_aleatorio = 'XcV34kP56tF'  

// SQL to insert

Insert into tabla_asociaciones (nombre_archivo_real, codigo_aleatorio) 
values ($nombre_archivo, $codigo_aleatorio); //Al implementarlo usar prepared statements para evitar sql injection

Note: As the path is the same for all, it is not necessary to save it in the database, it can be pasted later.

3) Already with the random string you can build the url and download it as a link in the response. The url must have a first part that identifies that you want to download the file and the last part of the path, it would be the random identifier.

Url example:

http://www.misitio.com/descargar/XcV34kP56tF

CLARIFICATION based on @ J.Doe's comment on development environment:

In the case of local development, in the hosts file for both linux and Windows you can relate www.mysite.com with 127.0.0.1 which is the ip of localhost. With this change and setting the XAMP virtual host to www.misition.com, we can develop locally using the FQDN. To go to the real site on iternet, afterwards, you just have to comment the line of the file with #.

For the second part:

1) A rewrite rule is implemented (for example in Apache's .htaccess) that recognizes the first part of the request path that indicates downloading a file, then applies the rule that takes the part of the random identifier, and redirects to the php program that will do the download, adding the random part as a request parameter.

Following the example, the rewrite rule recognizes the path pattern 'download /' followed by the portion of the path that has the association to the real file. And in order to use the latter as a parameter we capture it in a group.

Acknowledges: http://www.misitio.com/descargar/XcV34kP56tF
And save as group 1: XcV34kP56tF

Finally it redirects to the new url:

http://www.misitio.com/descargar_archivo.php?archivo=XcV34kP56tF

In the .htaccess file the following lines are added to achieve this:

RewriteEngine On
RewriteRule ^descargar\/(.+)$ http://misitio.com/descargar_archivo.php?archivo=$1 [R=301,L]

2) The download_file.php program decodes the random id that comes as a parameter using the association that was made beforehand in the table, and proceeds to download the file reading it with its real name.

The program in our example would be descargar_archivo.php and the archivo parameter.

download_file.php (Pseudocode to display the relevant logic)

$llave_al_archivo = $_GET['archivo'];

<Sanitizar $llave_llave_al_archivo>

//Buscar en la base de datos el archivo real
Select nombre_archivo_real 
from tabla_asociaciones 
where codigo_aleatorio = $llave_al_archivo;

if(resultado_query !== false){
    $nombre_real = resultado_query['nombre_archivo_real'];
    $archivo_real_con_path = '/home/archivos_privados/' . $nombre_real
    if(file_exists($archivo_real_con_path) === true){
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename=' . $nombre_real);
        header('Pragma: no-cache');
        readfile($archivo_real_con_path);

        //Correr query para eliminar la asociación de forma que no se pueda volver a usar

        delete from tabla_asociaciones 
        where codigo_aleatorio = $llave_al_archivo;

    }else{
        //Reportar error interno, quizás el archivo existía y lo borraron.
    }    

}else{
  //Reportar error archivo no existe o ya fue bajado.
} 

3) Once the file is downloaded, the program removes the association from the database table. Therefore, if it is used again, it will no longer find the file and it will give that link as invalid and it would return an error message in the response.

Scroll to Top