Envoyé par unreal
Il arrive souvent à ce qu'on souhaite proposer des fichiers à télécharger sur un site. Pour cela, il existe plusieurs solutions selon le niveau de protection des fichiers dont on a besoin.
Solution simplicite
Il s'agit simplement d'une redirection dont la seule utilité c'est compter le nombre de téléchargements (pour l'afficher sur le site).
La méthode classique consiste alors à programmer un download.php qui acceptera comme paramètre l'ID du fichier ou le path vers le fichier.
Exemple d'implémentation.
download.php
Même si la détection du '..' n'est pas vraiment nécessaire dans ce cas précis (vu que la redirection ne donnera aucunement accès à des fichiers en dehors du www_root du serveur Web), cela reste une bonne pratique. Et c'est une bonne idée d'adopter les bonnes pratiques dès le début.
L'inconvénient de cette méthode c'est qu'il est facile de connaître l'adresse réelle du fichier, donc il est possible de contourner download.php. Si le but de download.php est seulement de générer des statistiques de téléchargements, ceci n'est guère un problème. Par contre, pour sécuriser les fichiers, il va falloir faire mieux.
Solution sécurisée
Avec cette méthode, il n'est plus question de placer les fichiers à télécharger dans le www_root et de réaliser une redirection, mais d'envoyer les données à travers download.php.
L'exemple qui suit montre une implémentation (parmi d'autres).
download.php
Quelques remarques :
Voilà c'est fini, à vous de jouer maintenant.
Solution simplicite
Il s'agit simplement d'une redirection dont la seule utilité c'est compter le nombre de téléchargements (pour l'afficher sur le site).
La méthode classique consiste alors à programmer un download.php qui acceptera comme paramètre l'ID du fichier ou le path vers le fichier.
Exemple d'implémentation.
download.php
<?php
$p = '';
if (isset($_GET['p']))
$p = $_GET['p'];
if ($p) {
// On n'autorise pas les chemins '..'
if (preg_match('/\.\./', $p)) {
Header ('Location: ./');
exit();
}
// Code pour mettre a jour
// les stats de telechargements
Header ('Location: ./files'.$p);
} else {
Header ('Location: ./');
}
?>
$p = '';
if (isset($_GET['p']))
$p = $_GET['p'];
if ($p) {
// On n'autorise pas les chemins '..'
if (preg_match('/\.\./', $p)) {
Header ('Location: ./');
exit();
}
// Code pour mettre a jour
// les stats de telechargements
Header ('Location: ./files'.$p);
} else {
Header ('Location: ./');
}
?>
Même si la détection du '..' n'est pas vraiment nécessaire dans ce cas précis (vu que la redirection ne donnera aucunement accès à des fichiers en dehors du www_root du serveur Web), cela reste une bonne pratique. Et c'est une bonne idée d'adopter les bonnes pratiques dès le début.
L'inconvénient de cette méthode c'est qu'il est facile de connaître l'adresse réelle du fichier, donc il est possible de contourner download.php. Si le but de download.php est seulement de générer des statistiques de téléchargements, ceci n'est guère un problème. Par contre, pour sécuriser les fichiers, il va falloir faire mieux.
Solution sécurisée
Avec cette méthode, il n'est plus question de placer les fichiers à télécharger dans le www_root et de réaliser une redirection, mais d'envoyer les données à travers download.php.
L'exemple qui suit montre une implémentation (parmi d'autres).
download.php
<?php
$UploadDir = '/path/to/download/folder'; // Pas de / final !
$p = '';
if (isset($_GET['p']))
$p = $_GET['p'];
if ($p) {
// On n'autorise pas les chemins '..'
if (preg_match('/\.\./', $p)) {
Header ('Location: ./');
exit();
}
// Code pour mettre a jour
// les stats de telechargements
// Code pour gerer les droits d'acces
$path = $UploadDir . $p;
if (!is_file($path))
exit();
@ob_end_clean();
@ini_set('zlib.output_compression', 'Off');
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public');
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="' . basename($path) . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($path));
$handle = fopen($path, 'rb');
do {
$data = fread($handle, 8192);
if (strlen($data) == 0) {
break;
}
echo($data);
} while (true);
fclose($handle);
exit();
} else {
Header ('Location: ./');
}
?>
$UploadDir = '/path/to/download/folder'; // Pas de / final !
$p = '';
if (isset($_GET['p']))
$p = $_GET['p'];
if ($p) {
// On n'autorise pas les chemins '..'
if (preg_match('/\.\./', $p)) {
Header ('Location: ./');
exit();
}
// Code pour mettre a jour
// les stats de telechargements
// Code pour gerer les droits d'acces
$path = $UploadDir . $p;
if (!is_file($path))
exit();
@ob_end_clean();
@ini_set('zlib.output_compression', 'Off');
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public');
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="' . basename($path) . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($path));
$handle = fopen($path, 'rb');
do {
$data = fread($handle, 8192);
if (strlen($data) == 0) {
break;
}
echo($data);
} while (true);
fclose($handle);
exit();
} else {
Header ('Location: ./');
}
?>
Quelques remarques :
- La détection '..' est particulièrement importante ici, sans quoi il serait possible d'accéder à tous les fichiers du disque.
- Si après vérification l'utilisateur n'a pas les droits nécessaires, il suffit de quitter (exit();) ou de renvoyer sur une autre page (Header ('Location: page.php');).
- Cette implémentation charge le fichier par bloc de 8ko -- ceci est important car il n'est pas concevable de charger entièrement le fichier à télécharger en mémoire.
Voilà c'est fini, à vous de jouer maintenant.
Posté le 12/07/05 à 19:47