Uploader des images sur un serveur web - Apprendre-PHP.com

Rechercher
Boutique en ligne, solution e-commerce, script PHP et PERL : RAYNETTE

Uploader des images sur un serveur web

Uploader des images sur un serveur web

Beaucoup d'applications web ont recours à des modules d'upload d'images. Parmi elles nous pouvons citer les galleries d'images ou bien encore les systèmes d'avatars de forums type PHPBB, VBulletin ou IPB (pour ne citer que les plus connus). Les programmes permettant ce genre de fonctionnalités peuvent-être parfois très évolués car ils se chargent de contrôler l'intégrité du fichier ou bien même de le redimensionner à la volée.

Le script qui suit montre le fonctionnement d'un système d'upload d'images sur le serveur Web. Ses principales caractéristiques sont les suivantes :

  • Il est entièrement configurable grâce aux constantes situées en tête du script.
  • Il dispose d'un tableau d'extensions d'image autorisées. Les extensions possibles sont celles acceptées par la fonction getimagesize() de PHP.
  • Il crée le répertoire cible s'il n'existe pas.
  • Il contrôle l'extension du fichier envoyé.
  • Il contrôle le type de l'image.
  • Il contrôle les dimensions de l'image (largeur et hauteur).
  • Il contrôle le poids de l'image.
  • Il upload l'image sur le serveur avec un nouveau nom.
  • Il retourne un message de réussite ou d'erreur à l'utilisateur.
  • Il est sécurisé.

Le script complet

Script PHP de l'upload d'image
<?php
 
/************************************************************
 * Script realise par Emacs
 * Crée le 19/12/2004
 * Maj : 23/06/2008
 * Licence GNU / GPL
 * webmaster@apprendre-php.com
 * http://www.apprendre-php.com
 * http://www.hugohamon.com
 *
 * Changelog:
 *
 * 2008-06-24 : suppression d'une boucle foreach() inutile
 * qui posait problème. Merci à Clément Robert pour ce bug.
 *
 *************************************************************/
 
/************************************************************
 * Definition des constantes / tableaux et variables
 *************************************************************/
 
// Constantes
define('TARGET', '/files/'); // Repertoire cible
define('MAX_SIZE', 100000); // Taille max en octets du fichier
define('WIDTH_MAX', 800); // Largeur max de l'image en pixels
define('HEIGHT_MAX', 800); // Hauteur max de l'image en pixels
 
// Tableaux de donnees
$tabExt = array('jpg','gif','png','jpeg'); // Extensions autorisees
$infosImg = array();
 
// Variables
$extension = '';
$message = '';
$nomImage = '';
 
/************************************************************
 * Creation du repertoire cible si inexistant
 *************************************************************/
if( !is_dir(TARGET) ) {
if( !mkdir(TARGET, 0755) ) {
exit('Erreur : le répertoire cible ne peut-être créé ! Vérifiez que vous diposiez des droits suffisants pour le faire ou créez le manuellement !');
}
}
 
/************************************************************
 * Script d'upload
 *************************************************************/
if(!empty($_POST))
{
// On verifie si le champ est rempli
if( !empty($_FILES['fichier']['name']) )
{
// Recuperation de l'extension du fichier
$extension = pathinfo($_FILES['fichier']['name'], PATHINFO_EXTENSION);
 
// On verifie l'extension du fichier
if(in_array(strtolower($extension),$tabExt))
{
// On recupere les dimensions du fichier
$infosImg = getimagesize($_FILES['fichier']['tmp_name']);
 
// On verifie le type de l'image
if($infosImg[2] >= 1 && $infosImg[2] <= 14)
{
// On verifie les dimensions et taille de l'image
if(($infosImg[0] <= WIDTH_MAX) && ($infosImg[1] <= HEIGHT_MAX) && (filesize($_FILES['fichier']['tmp_name']) <= MAX_SIZE))
{
// Parcours du tableau d'erreurs
if(isset($_FILES['fichier']['error'])
&& UPLOAD_ERR_OK === $_FILES['fichier']['error'])
{
// On renomme le fichier
$nomImage = md5(uniqid()) .'.'. $extension;
 
// Si c'est OK, on teste l'upload
if(move_uploaded_file($_FILES['fichier']['tmp_name'], TARGET.$nomImage))
{
$message = 'Upload réussi !';
}
else
{
// Sinon on affiche une erreur systeme
$message = 'Problème lors de l\'upload !';
}
}
else
{
$message = 'Une erreur interne a empêché l\'uplaod de l\'image';
}
}
else
{
// Sinon erreur sur les dimensions et taille de l'image
$message = 'Erreur dans les dimensions de l\'image !';
}
}
else
{
// Sinon erreur sur le type de l'image
$message = 'Le fichier à uploader n\'est pas une image !';
}
}
else
{
// Sinon on affiche une erreur pour l'extension
$message = 'L\'extension du fichier est incorrecte !';
}
}
else
{
// Sinon on affiche une erreur pour le champ vide
$message = 'Veuillez remplir le formulaire svp !';
}
}
?>
Le formulaire HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<title>Upload d'une image sur le serveur !</title>
</head>
<body>
<?php
if( !empty($message) )
{
echo '<p>',"\n";
echo "\t\t<strong>", htmlspecialchars($message) ,"</strong>\n";
echo "\t</p>\n\n";
}
?>
<!-- Debut du formulaire -->
<form enctype="multipart/form-data" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="post">
<fieldset>
<legend>Formulaire</legend>
<p>
<label for="fichier_a_uploader" title="Recherchez le fichier à uploader !">Envoyer le fichier :</label>
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_SIZE; ?>" />
<input name="fichier" type="file" id="fichier_a_uploader" />
<input type="submit" name="submit" value="Uploader" />
</p>
</fieldset>
</form>
<!-- Fin du formulaire -->
</body>
</html>


Les commentaires

1. Par prout le mardi 04 décembre 2007 à 22:10

Bonjour,

Votre script est il protégé contre les extensions fausses?

Merci pour votre site.

2. Par Emacs le mercredi 05 décembre 2007 à 13:01

Bonjour,

Qu'appellez-vous "extensions fausses" ? La fonction pathinfo() renvoie la véritable extension du fichier envoyé.

Cordialement.

3. Par Bruno le mercredi 16 janvier 2008 à 00:12

Bonjour .

Bravo pour votre site très soigné .

Bon tuto , mais il manque la protection contre les insertions de code dans la source mème de l'image , seul readfile peut contrer cette ultime attaque .

Bruno

4. Par Emacs le mercredi 16 janvier 2008 à 09:09

Effectivement Bruno ! Je corrige ça dans l'après midi Emoticone clin d'oeil

5. Par Emacs le mercredi 16 janvier 2008 à 20:08

Correction apportée. L'image est renommée à la volée avec une chaine MD5 aléatoire générée par un uniqid().

6. Par Bruno le jeudi 17 janvier 2008 à 08:08

Oui , en fait , mon post n'a pas un rapport direct avec ton tutoriel .

Il intervient seulement si l'on doit afficher des images du client et dont on ignore l'origine .

http://www.nexen.net/actualites/securite/17226-attaque_par_image_gif.php


Bruno

7. Par Emacs le jeudi 17 janvier 2008 à 09:09

Oui je suis d'accord avec toi. Sachant qu'en plus j'ai lu récemment le livre de Damien Séguy. Il parle de la sécurité des noms de fichiers. On peut effectivement avoir des attaques XSS avec un nom de fichier malsaint.

8. Par bruno le jeudi 17 janvier 2008 à 22:10

Tu pourras effacer ces messages , soit dir en passant .
Je crois qu'on ne s'est pas compris , je parlais de php dans le code mème de l'image , le format gif supporte du code php en son sein . Contre ça , on ne peut rien faire , sauf le lire en désactivant le moindre code .

http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html

9. Par Emacs le vendredi 18 janvier 2008 à 00:12

Oui c'est exact avec IE 6. Il faut ouvrir le fichier gif et contrôler qu'il n'y ait pas de code PHP à l'intérieur.

10. Par Mazzu le dimanche 20 janvier 2008 à 02:02

J'ai fait très récemment un script d'upload qui ressemble beaucoup à ça. C'est marrant Emoticone content

Je vais me permettre quelques petites remarques.
- En rapport avec la remarque de Bruno, il faut ou bien faire comme phpclasses.org l'explique ou bien s'assurer que le fichier n'a pas une extension de fichier qui soit interprété par PHP (comme c'est fait ici) ou bien placer les images dans un répertoire à part qui contient un .htaccess avec la ligne : "php_flag engine Off" et s'assurer de ne pas écraser le .htaccess
- Ensuite, juste pour avoir quelque chose de plus propre, j'aurais changé les lignes suivantes,
ligne 49 : if(!empty($_FILES['fichier']['name']) && $_FILES['fichier']['error'] === UPLOAD_ERR_OK)
ligne 58 : $infosImg = @getimagesize($_FILES['fichier']['tmp_name']);
ligne 61 : if($infosImg !== FALSE && $infosImg[2] >= 1 && $infosImg[2] <= 14)

11. Par Emacs le dimanche 20 janvier 2008 à 10:10

Bonjour Mazzu,

Ta solution du .htaccess est pas mal mais ce n'est pas la meilleure. Pour sécuriser encore un peu mieux, il faudrait placer les fichiers uploadés dans un répertoire "hors web". Néanmoins la création de répertoires au dessus de la racine web est très rarement autorisée sur des serveurs mutualisés.

Quant à la constante UPLOAD_ERR_OK, tu viens de me l'apprendre Emoticone content Je vais l'intégrer au script je pense.

Pour la ligne 58, le @ ne sert strictement rien si le serveur est bien configuré pour masquer les erreurs en production.

A la ligne 61, effectivement je n'ai pas vérifié la valeur de retour de la variable $infosImg.

Je te remercie pour ton commentaire constructif Mazzu Emoticone clin d'oeil

Hugo.

12. Par Mazzu le dimanche 20 janvier 2008 à 18:06

You're Welcome !

Pour la constante UPLOAD_ERR_OK, je n'ai rien inventé, tout est là Emoticone clin d'oeil
http://fr3.php.net/manual/fr/features.file-upload.php

13. Par bruno le mardi 22 janvier 2008 à 15:03

Bonjour a vous .
Finalement , la meilleure solution serait d'utiliser le format png a l'instar du gif . Avec le patch png pour IE .
Ce n'est qu'une idée .

Bruno

14. Par rocawear le mardi 29 janvier 2008 à 03:03

Bonjour a vous tous,
j'ai deja subit une attaque dans le passé car les fichier de type .php.ext fonctionais et php les executais.

pour resoudre ce probleme j'ai du faire une cimple condition toute bête !

Voila:

if(preg_match('#php#isU',$fichier)){ // Si le fichier comporte .php.jext, ont renome le php en 484 ( a vous de choisir )
$fichier = str_replace('php','484',$fichier);
}

15. Par Raherivo le vendredi 14 mars 2008 à 13:01

Bonjour à tous,
Je demanderai à vous, donnez moi le code du modification d'upload image s'il vous plaît! je n'en trouvais plus.
Merci beaucoup à bientôt!

16. Par Jeux le mercredi 02 juillet 2008 à 09:09

Merci ce script me sera bien utile, surtout avec toutes les modifications apportées petit à petit

Ajouter un commentaire

Votre avis
Se souvenir de mes informations