L'upload de fichier (sécurisé)
Noter ce cours :
Les formulaires d'upload sont de plus en plus répandus sur un site Web. PHP permet de gérer l'upload très simplement. Il faudra par contre sécuriser cet upload pour éviter aux gens mal intentionnés d'envoyer du code PHP (par exemple dans un formulaire d'upload d'avatar, nous souhaitons recevoir une image...), c'est aussi ce que nous verrons plus loin dans cet article.
Un formulaire d'upload en XHTML :
Un formulaire d'upload contient obligatoirement un champ de type file qui dispose d'un bouton parcourir pour que vous puissiez envoyer votre fichier. Voici un exemple de formulaire basique que nous utiliserons pour tous les exemples présents dans cet article :
<form action="upload.php" method="post" enctype="multipart/form-data">
<p><input type="file" name="fichier_source" /><input type="submit" value="Envoyer" /></p>
</form>
Ce formulaire va nous permettre d'envoyer à notre page upload.php toute sorte de fichiers que nous analyserons ensuite.
La variable superglobale $_FILES :
La variable superglobale $_FILES vous permet d'avoir pleins d'informations sur le fichier envoyé. Attention cependant : ces informations sont fournies par le navigateur Web, elles peuvent donc être manipulées. Attention aux failles de sécurité (notamment pour le type mime du fichier). Nous utiliserons pour tous nos exemples le nom de champ fichier_source dans notre formulaire XHTML. Vous devez donc remplacer dans PHP fichier_source par le nom de votre champ file.
- $_FILES['fichier_source']['name'] : renvoit le nom du fichier stocké sur la machine du client qui envoit le fichier.
- $_FILES['fichier_source']['tmp_name'] : renvoit le nom du fichier stocké sur le serveur (le fichier a donc été reçu).
- $_FILES['fichier_source']['type'] : renvoit le type mime du fichier envoyé. Attention aux failles de sécurité.
- $_FILES['fichier_source']['size'] : renvoit la taille du fichier en octets.
- $_FILES['fichier_source']['error'] : renvoit éventuellement un code d'erreur pendant le transfert.
Se préparer à la réception du fichier :
Nous devons en cas de réception d'un fichier utiliser PHP pour le traiter. Affichons un exemple simple pour savoir si le formulaire a été envoyé :
<?php
if(!empty($_FILES['fichier_source']['tmp_name']))
{
//Un fichier a été envoyé, nous pouvons le traiter
}
?>
Savoir si le fichier a été uploadé correctement :
Pour éviter les erreurs futures, il est utile de savoir si le fichier a été uploadé correctement. On utilise pour cela la fonction is_uploaded_file().
Exemple :
<?php
if(is_uploaded_file($_FILES['fichier_source']['tmp_name']))
{
//C'est bon
}
?>
Copier un fichier uploadé sur le serveur dans un répertoire de destination :
On utilise pour déplacer le fichier uploadé du répertoire temporaire vers le répertoire de destination la fonction move_uploaded_file().
Exemple :
<?php
if(is_uploaded_file($_FILES['fichier_source']['tmp_name']))
{
move_uploaded_file($_FILES['fichier_source']['tmp_name'],'repertoire/image.jpg'); //L'image est copiée sous le nom 'image.jpg' dans le répertoire 'repertoire'
}
?>
Les bases acquises maintenant vont nous permettre de les mettre en pratique avec un exemple qui va consister à recevoir une image (par upload). Cette image doit être de type jpeg obligatoirement (toutes les extensions de fichiers sont acceptées), sa taille ne doit pas excéder 50 Ko. Nous l'enregistrerons sous le nom image.jpeg.
Nous allons tout d'abord reprendre le code XHTML du formulaire de départ :
<form action="upload.php" method="post" enctype="multipart/form-data">
<p><input type="file" name="fichier_source" /><input type="submit" value="Envoyer" /></p>
</form>
Passons ensuite au code PHP de la page upload.php :
<?php
if(!empty($_FILES['fichier_source']['tmp_name']) AND is_uploaded_file($_FILES['fichier_source']['tmp_name']))
{
//On va vérifier la taille du fichier en ne passant pas par $_FILES['fichier_source']['size'] pour éviter les failles de sécurité
if(filesize($_FILES['fichier_source']['tmp_name'])<51200)
{
//On vérifie maintenant le type de l'image à l'aide de la fonction getimagesize()
list($largeur, $hauteur, $type, $attr)=getimagesize($_FILES['fichier_source']['tmp_name']);
//Si le Type est JPEG (correspond au chiffre 2) on copie l'image
if($type===2)
{
//Copie le fichier dans le répertoire de destination
if(move_uploaded_file($_FILES['fichier_source']['tmp_name'], 'repertoire/image.jpg'))
{
//Le fichier a été uploadé correctement
echo 'Ok, fichier envoyé correctement';
}
else
{
//Erreur
echo 'Erreur lors de la copie du fichier';
}
}
}
}
?>
Cet exemple est sécurisé contre les attaques par changement de type de fichier, les extensions n'influençent en rien ce script et la modification des en-têtes HTTP non plus. Il faut par contre faire attention aux noms de fichiers locaux pouvant contenir des caractères spéciaux. Je vous conseille d'utiliser pour cela les expressions régulières.