Empecher Le Telechargement Direct de Fichiers
Empecher Le Telechargement Direct de Fichiers
Empecher Le Telechargement Direct de Fichiers
tlchargement direct
de fichiers
Par Noxalus
www.openclassrooms.com
Sommaire
Sommaire ........................................................................................................................................... 2
Empcher le tlchargement direct de fichiers .................................................................................. 3
La page de tlchargement .............................................................................................................................................. 3
Protgeons nos fichiers ..................................................................................................................................................... 5
Passer outre la protection ................................................................................................................................................. 6
Forcer le tlchargement des fichiers ............................................................................................................................... 6
Une faille particulirement dangereuse ............................................................................................................................ 9
Partager ..................................................................................................................................................................................................................... 12
www.openclassrooms.com
Sommaire 3/13
Par Noxalus
Si vous tes ici, c'est que vous avez un site internet sur lequel se trouvent des fichiers dont vous voulez matriser le
tlchargement. Beaucoup de sites disposent d'une page qui vous donne accs aux fichiers que vous souhaitez tlcharger ; le
plus souvent, elle ne fait que vous rediriger vers le fichier cibl coups de :
Code : PHP
<?php header('Location:
http://www.monsite.com/fichiers/le_fichier_a_telecharger'); ?>
et permet l'administrateur, par exemple, de compter le nombre de fois qu'il a t tlcharg via une base de donnes ou un
fichier texte. Une telle redirection prsente un inconvnient majeur : le lien direct vers le fichier est rvl l'utilisateur, qui peut
donc le tlcharger sans passer par la page de votre site prvue cet effet. Ce qui fausse toutes vos statistiques et conduit
mme au vol de fichiers , c'est--dire que d'autres administrateurs peuvent rendre publics les liens directs de vos fichiers sans
que leurs utilisateurs ne sachent qu'ils sont hbergs sur votre site ! Malgr le trafic ainsi gnr, ces personnes ne visitent pas
votre site.
Heureusement, ce tutoriel vous apprendra une mthode efficace pour rsoudre ce problme et vous pargnera, je l'espre, de
mauvaises surprises.
Sommaire du tutoriel :
La page de tlchargement
Protgeons nos fichiers
Passer outre la protection
Forcer le tlchargement des fichiers
Une faille particulirement dangereuse
La page de tlchargement
Avant de commencer scuriser quoi que ce soit, nous allons voir quoi ressemble notre page de tlchargement ou devrais-
je dire nos pages , car nous allons prendre l'habitude de sparer les diffrents fichiers afin de ne pas mlanger PHP et HTML.
<?php
// Si l'utilisateur a demand le tlchargement d'un fichier
if(!empty($_GET['fichier']))
// On lance le tlchargement du fichier
else
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 4/13
require('erreur.php');
?>
Ici, rien de bien compliqu : nous vrifions seulement si le tlchargement a t demand via une variable
$_GET['fichier']. Si oui, nous le lanons (nous verrons comment par la suite) ; sinon, nous afficherons un message
prvenant l'utilisateur qu'il n'a demand le tlchargement d'aucun fichier.
Afin de faire quelque chose d' peu prs propre, ledit message sera inclus dans une page, que nous pourrions nommer
erreur.php, grce la fonction require() :
Code : PHP
<!DOCTYPE html>
<html>
<head>
<title>Tlchargement d'un fichier</title>
</head>
<body>
<p>
Dsol, ce fichier n'existe pas.
</p>
</body>
</html>
La partie qui nous intresse est celle dans le cas o l'on souhaite tlcharger un fichier, donc quand la variable
$_GET['fichier'] n'est pas nulle, dans le fichier telecharger.php.
Avant, la mthode consistait rediriger directement l'utilisateur sur le fichier qu'il avait demand. En imaginant que le dossier qui
contient tous vos fichiers tlcharger ait pour nom fichiers et qu'il se trouve la racine, nous aurions fait quelque chose de ce
type :
<?php
if(!empty($_GET['fichier']))
{
$chemin = 'fichiers/' . $_GET['fichier'];
if(file_exists($chemin))
header('Location: http://monsite.com/' . $chemin);
else
require('erreur.php');
}
else
require('erreur.php');
?>
Nous venons d'utiliser la fonction file_exists(), qui vrifie que le fichier existe bien avant d'en faire quoi que ce soit.
Ainsi, dans le cas o un fichier test.txt, par exemple, se trouverait dans le dossier des fichiers (fichiers/), nous aurions
seulement donner l'utilisateur le lien http://www.monsite.com/telecharger.php?fichier=test.txt pour qu'il puisse le tlcharger
(ou du moins l'ouvrir, dans le cas de notre fichier texte). Nous pourrions donc, pour reprendre l'exemple du dbut, rajouter un
compteur afin de connatre le nombre de fois qu'il a t tlcharg.
<?php
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 5/13
if(!empty($_GET['fichier']))
{
$chemin = 'fichiers/' . $_GET['fichier'];
if(file_exists($chemin))
{
// On incrmente le nombre de fois que le fichier a t
tlcharg !
incrementer_compteur($chemin); // C'est cette fonction qui va
s'en occuper !
header('Location: http://monsite.com/' . $chemin);
}
else
require('erreur.php');
}
else
require('erreur.php');
?>
Cependant, cette mthode ne rsout pas le problme du vol de fichiers puisqu'elle communique l'utilisateur le lien direct vers le
fichier qu'il souhaite tlcharger. Il lui sera non seulement possible d'y accder de nouveau sans passer par la page de
tlchargement, mais votre compteur ne vous sera plus d'aucune utilit. Nous allons voir tout de suite comment empcher cela.
Je ne vous expliquerai pas le fonctionnement d'un fichier .htaccess, ni mme ne rentrerai dans les dtails de ce qu'il est possible
de faire grce lui, puisque d'autres l'ont fait avant moi. Je vais simplement vous dire en quoi il nous sera utile. Pour faire court,
une des possibilits du .htaccess est d'empcher l'accs des dossiers entiers avec ce petit bout de texte :
Code : Apache
En ajoutant ce code un fichier texte nomm .htaccess et en plaant ce dernier dans le dossier de votre choix, vous tomberez sur
une jolie erreur 403 : Forbidden (ce qui signifie Interdit , pour les anglophobes) si vous essayez d'atteindre un lment
prsent dans ce dossier ou dans un de ses sous-dossiers.
Code : Console
Forbidden
Youhouhou, c'est exactement ce que nous voulions ! Maintenant, si ces vilains fraudeurs veulent accder directement aux
fichiers, ils ne le pourront plus.
C'est gnial ! Mais Euh La page de tlchargement ne fonctionne plus C'est normal ?!
Bien sr, notre page ne fait que rediriger l'utilisateur vers le fichier qui va bien. Donc, si nous empchons l'accs ces fichiers, il
ne pourra pas non plus les tlcharger en passant par notre page. Ce qui n'est pas sans poser problme, n'est-ce pas ?
Heureusement, le but de ce tutoriel est de vous expliquer comment contourner cette protection.
Je prfre que les choses soient claires. TOUS les fichiers qui se trouvent dans le mme dossier que le .htaccess, ainsi
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 6/13
L o a nous arrange, c'est qu'il existe une fonction readfile() qui permet, comme le dit la documentation, de lire un fichier et de
l'envoyer dans le buffer de sortie. On ne redirige donc plus vers le fichier, on prend carrment son contenu !
En reprenant le code prcdent, nous pouvons donc remplacer le header() (la redirection) par ceci :
Code : PHP
<?php
if(!empty($_GET['fichier']))
{
$chemin = 'fichiers/' . $_GET['fichier'];
if(file_exists($chemin))
readfile($chemin);
else
require('erreur.php');
}
?>
Super ! Mais Euh Quand j'essaye de tlcharger une image, de nombreux symboles bizarres s'affichent. C'est
normal ?!
Encore une fois, oui, c'est tout fait normal. Vous avez dj tent d'ouvrir une image avec le bloc-notes ? Eh bien, cela devrait
donner la mme chose ! En effet, readfile() ne fait que lire le contenu du fichier et ne cherche pas l'afficher en fonction de son
type.
Cela dit, malgr le .htaccess, le contenu de notre fichier est bien lu et affich. La solution que nous allons employer pour ne plus
afficher le contenu du fichier est d'en forcer le tlchargement.
Vous avez d remarquer plusieurs reprises que, quelle que soit la technique utilise, scurise ou non, tous les fichiers qui
peuvent tre ouverts par le navigateur le sont effectivement condition, bien entendu, de ne pas avoir modifi la
configuration du navigateur. Donc, en temps normal, plutt que de tlcharger un fichier au format texte ou XML, une image, un
PDF et beaucoup d'autres, votre navigateur l'ouvrira. Vous pouvez ensuite tlcharger le fichier en question grce un clic droit
puis Enregistrer sous , mais ce n'est vraiment pas pratique.
Nanmoins, il est possible, avec les en-ttes HTTP, de forcer la main aux navigateurs et de les obliger tlcharger ledit fichier
avec ces quelques lignes :
Code : PHP
<?php
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' .
basename($chemin));
header('Content-Transfer-Encoding: binary');
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 7/13
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($chemin));
readfile($chemin);
exit;
?>
Ce code est extrait de la documentation PHP de la fonction readfile() et n'a subi qu'une simple modification. En effet, la
gestion de la tamporisation de sortie est devenue inutile dans notre cas.
Ne vous inquitez pas, je ne vais pas vous laisser avec ce code sans aucune explication.
Pour commencer, notons qu'il y a beaucoup d'appels la fonction header(). Nous l'avons vu au dbut de ce cours, cette fonction
assure une redirection (vers un fichier par exemple) si elle est utilise avec
'Location: [adresse de redirection]'. Elle permet en ralit bien plus que cela, notamment de modifier l'en-tte
HTTP reu par le navigateur. Ainsi, vous pourrez facilement lui dire qu'il s'agit d'un fichier tlcharger et l'obliger l'interprter
comme tel.
Ligne 2 : spcifie au navigateur que les donnes qu'il va recevoir doivent tre considres comme un fichier
tlcharger.
Ligne 3 : indique que le flux de donnes qui va suivre est de type flux d'octet . Comme n'importe quel fichier peut tre
considr ainsi, il n'est pas ncessaire d'en connatre le type MIME exact (qui diffre en fonction de l'extension).
Ligne 4 : attribue un nom au fichier. Par consquent, le nom qui apparatra dans la popup de tlchargement sera celui
indiqu aprs filename=.
Ligne 5 : prcise que le fichier traiter devra tre envoy en binaire. En d'autres termes, les donnes seront conserves
telles quelles afin d'viter les problmes d'encodage et de transformation non voulus.
Lignes 6 8 : ces lignes ordonnent au navigateur de ne pas mettre les fichiers en cache pour que le tlchargement soit
dclench chaque fois.
Ligne 9 : donne au navigateur la taille du fichier, sans laquelle il ne pourrait afficher correctement la barre de progression
ni donner le pourcentage dj tlcharg et encore moins estimer le temps restant.
Pour aller un peu plus loin avec les en-ttes HTTP, vous trouverez d'autres informations sur ce site.
En ce qui concerne les fonctions PHP maintenant : basename() permet, partir d'un chemin, de rcuprer uniquement le nom d'un
fichier ; filesize() (les anglophones l'auront dj devin) en indique la taille. Quant la fonction exit(), qui clt cette srie d'appels,
elle sert stopper l'excution du script pour ne pas rajouter de donnes la suite du fichier sous peine de le corrompre. De plus,
il est inutile de charger le reste d'une page qui ne sera, en thorie, pas visible.
Pour continuer garder un code clair et propre, nous allons crer une fonction, appele readfile(), la seule fin d'envoyer le
header personnalis et mme de faire la vrification.
Vous devez certainement dj possder un fichier fonctions.php dans lequel vous mettez toutes les fonctions de votre site.
Il vous suffit de lui ajouter la fonction suivante :
<?php
function telecharger_fichier($fichier)
{
$chemin = 'fichiers/' . $fichier;
if(file_exists($chemin))
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' .
basename($chemin));
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 8/13
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-
check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($chemin));
readfile($chemin);
exit;
}
else
require('erreur.php');
}
?>
Si vous voulez faire en sorte d'afficher les images plutt que d'obliger l'utilisateur les tlcharger, vous pouvez
modifier cette fonction comme ceci :
<?php
function telecharger_fichier($fichier)
{
$chemin = 'fichiers/' . $fichier;
$images_ext = array('jpg', 'jpeg', 'png', 'bmp', 'gif');
if(file_exists($chemin))
{
$image_format = substr(strrchr($chemin, '.'), 1);
if(in_array($image_format, $images_ext))
header('Content-Type: image/' . $image_format);
else
{
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' .
basename($chemin));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($chemin));
}
readfile($chemin);
exit;
}
else
require('erreur.php');
}
?>
Le principe de cette mthode est d'interdire l'accs direct vos images (clic droit Afficher l'image ) tout en
permettant leur affichage. Par contre, vu que c'est du bonus, je n'expliquerai pas en dtail les rajouts. S'il y a quelque
chose que vous ne comprenez pas, faites-le-moi savoir par MP ou dans les commentaires. Sinon, la documentation est
toujours l pour vous aider.
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 9/13
<?php
if(!empty($_GET['fichier']))
{
// N'oubliez pas d'inclure le fichier qui contient notre fonction
!
include('fonctions.php');
// On appelle la fonction qu'on a cre juste avant !
telecharger_fichier($_GET['fichier']);
}
else
require('erreur.php');
?>
Oui, imaginez qu'un utilisateur malveillant dcide de se rendre sur cette page : http://www.monsite.com/telecharger.php [...]
=../index.php, il sera alors en mesure de tlcharger directement le contenu de votre page d'accueil. Pire, avec cette technique,
l'ensemble des fichiers de votre site sera sa disposition et, pour peu qu'il connaisse sa structure, il pourra faire une copie
complte de votre site comme s'il avait accs votre FTP. Il n'aurait plus qu' rcuprer les identifiants de votre base de donnes
(que vous avez forcment stocks dans l'un de vos fichiers PHP) pour finir de voler votre site en entier !
Vous ne pensiez pas qu'un petit script comme celui-l se rvlerait aussi dangereux, n'est-ce pas ? C'est pourquoi il est important
de se prmunir contre cette faille. Heureusement, je vous propose une solution permettant de pallier ce problme.
La premire chose faire est d'empcher le visiteur d'utiliser le slash (/). Ainsi, il ne pourra pas retourner en arrire ou naviguer
dans les dossiers sa guise.
Oui, mais attends ! Si je fais a, je ne pourrais plus classer les fichiers tlchargeables qui se trouvent dans le dossier
fichiers/ l'intrieur de sous-dossiers. Tous les fichiers tlchargeables ne doivent-ils pas se trouver la racine du
dossier fichiers/ ?!
Pas ncessairement. C'est vous et vous seul de dfinir les rgles, et vous pouvez trs bien choisir de rajouter des variables
GET qui dtermineront dans quel sous-dossier le fichier demand se trouve.
Un petit exemple avec ce lien : http://www.monsite.com/telecharger.php [...] txt&dossier=1. Il suffit de faire en sorte que
l'identifiant 1 corresponde un dossier particulier pour la variable $_GET['dossier']. Nous pourrions organiser les
dossiers de la faon suivante :
1 = fichiers/jeux/ ;
2 = fichiers/musiques/ ;
3 = fichiers/videos/ ;
4 = fichiers/images/ ;
etc.
Code : PHP
<?php
function id_dossier($id)
{
$dossier_base = 'fichiers/';
switch($id)
{
case 1:
$dossier_base .= 'jeux/';
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 10/13
break;
case 2:
$dossier_base .= 'musiques/';
break;
case 3:
$dossier_base .= 'videos/';
break;
case 4:
$dossier_base .= 'images/';
break;
default:
break;
}
return $dossier_base;
}
?>
Mais ce n'est pas tout Mme si l'on bouche cette faille, il en reste une : quoi qu'il arrive, il y aura toujours dans votre dossier
des fichiers qui ne seront pas destins au tlchargement. C'est par exemple le cas pour notre .htaccess, qui devra forcment se
trouver cet endroit, et pour tous les fichiers cachs dont les noms commencent par un point. Il faut donc vrifier galement qu'il
n'y a pas de point au dbut du nom du fichier.
Pour ce faire, nous allons utiliser la fonction strpos() qui permet de rcuprer la position d'un caractre (ou de plusieurs
caractres dans notre cas) dans une chane.
La position d'un caractre dans une chane ? quoi cela va-t-il nous servir ?
une seule chose : vrifier qu'il y a ou non des caractres que nous voulons interdire. Si les caractres en question ne sont pas
trouvs, la fonction renverra FALSE.
a, c'tait pour le slash. Pour le point, en revanche, rcuprer la position du caractre va nous tre utile. Non, il n'est pas interdit
de mettre un point dans le nom d'un fichier, surtout si vous dcidez de laisser l'extension. Par contre, nous ne voulons surtout
pas que ce point se trouve au dbut du nom du fichier ; nous vrifierons donc la position du point.
En sachant tout cela, notre fichier de fonctions sera modifi de cette faon :
<?php
function id_dossier($id)
{
$dossier_base = 'fichiers/';
switch($id)
{
case 1:
$dossier_base .= 'jeux/';
break;
case 2:
$dossier_base .= 'musiques/';
break;
case 3:
$dossier_base .= 'videos/';
break;
case 4:
$dossier_base .= 'images/';
break;
default:
break;
}
return $dossier_base;
}
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 11/13
Ne vous inquitez pas, je ne me suis pas tromp en mettant trois signes gal . L'oprateur === signifie gal et de
mme type et, ici, la condition est ncessaire notre vrification, car 0 == FALSE mais 0 !== FALSE !
Nous nous assurons ainsi que l'utilisateur ne pourra pas naviguer l'extrieur du dossier dans lequel se trouvent tous les
fichiers tlcharger.
Vous ne devez donc pas j'insiste encore sur ce point placer dans ce dossier des fichiers qui ne sont pas censs
tre tlchargs, comme des fichiers PHP. Une bonne fois pour toutes : le dossier fichiers/ ne doit contenir que les
fichiers que vos utilisateurs sont autoriss tlcharger !
De ces changements dcoule une modification minime de notre fichier telecharger.php afin de prendre en compte
l'identifiant du dossier :
<?php
if(!empty($_GET['fichier']))
{
// N'oubliez pas d'inclure le fichier qui contient notre fonction
!
include('fonctions.php');
// On appelle la fonction qu'on a cre juste avant !
telecharger_fichier($_GET['fichier'], $_GET['dossier']);
}
else
require('erreur.php');
?>
La suite va vous prsenter une autre solution un peu plus aboutie ( noter qu'elle reste nanmoins complmentaire de la
premire). Cela veut dire qu'il est hautement prfrable d'appliquer cette solution en plus, et vous allez tout de suite comprendre
pourquoi.
Comme je l'ai dit au dbut de ce cours, sur de nombreux sites, les fichiers tlcharger sont grs par une base de donnes. Par
consquent, chaque fichier est li des informations comme un nom (plus joli que le nom du fichier), une description, un
compteur, etc. Ainsi, il vous suffirait de vrifier que le fichier que veut tlcharger l'utilisateur figure bien dans la base de
www.openclassrooms.com
Empcher le tlchargement direct de fichiers 12/13
Cette solution peut ne pas suffire lorsque vous permettez le tlchargement de fichiers PHP (pour des tutoriels par exemple) et
que ces derniers portent, parfois, le mme nom que ceux qui se trouvent sur votre site. Dans ce cas-l, il est important de vrifier
que le fichier que le visiteur souhaite tlcharger se trouve dans le bon dossier. C'est prcisment ce que nous venons de faire.
Code : PHP
<?php
/*
On imagine que la connexion se fait avec PDO et que l'objet
PDO se trouve dans la variable du nom de $pdo
*/
$nom_fichier = $pdo->quote($_GET['fichier']);
$sql = $pdo->query("SELECT COUNT(nom_fichier) FROM fichiers WHERE
nom_fichier = '$nom_fichier'");
if(file_exists($chemin) && strpos($fichier, '/') === FALSE &&
strpos($fichier, '.') !== 0 &&
$sql->fetchColumn() > 0)
{
// Appel de la fonction pour tlcharger le fichier
}
?>
Il ne s'agit l que d'un exemple qui ne fonctionnera qu' deux conditions. Premirement, que vous vous soyez connect avec
PDO en stockant l'objet PDO dans la variable qui rpond au doux nom de $pdo ; deuximement, que vous possdiez une table de
votre base de donnes, nomme fichiers, qui comporte une colonne nom_fichier et o sont stocks tous les fichiers
tlchargeables.
Quoi qu'il en soit, je pense que vous avez compris que cet exemple vous permet uniquement de comprendre le concept. vous
de l'adapter au fonctionnement de votre site.
Ouf, vos fichiers sont maintenant l'abri de ces mchants voleurs !
Vous pouvez dsormais vous concentrer sur des applications concrtes se servant de cette protection. Nous avons vu le cas du
compteur de tlchargement, mais d'autres horizons s'offrent vous : instaurer un systme de tlchargement payant, avec un
nombre limit de tlchargements ; ou encore empcher tout tlchargement pendant une plage d'heures dfinie pour viter une
surcharge du serveur. Et encore beaucoup d'autres choses que je vous laisse imaginer !
En tout cas, voil, ce tutoriel est fini. Il tait court (mme trs court), mais j'espre qu'il vous aura t aussi utile qu' moi.
D'ailleurs, je souhaite faire quelques remerciements. En effet, ce cours a t fait aprs que j'ai pos une question sur le forum et
laquelle deux personnes ont apport de trs bonnes rponses : maxima et orklah. Merci eux, donc, sans qui je n'aurais
certainement rien fait et serait rest, tout comme vous, dans l'ignorance. Ah, c'est beau le partage !
J'aimerais remercier une autre personne : Kyle Katarn, qui, en plus de m'avoir mis au courant de l'norme faille, m'a donn
beaucoup de conseils tant sur le fond que sur la forme pour faire de ce tutoriel ce qu'il est aujourd'hui. Merci encore !
Partager
www.openclassrooms.com