8731 sujets

Développement web côté serveur, CMS

Pages :
Modérateur
(reprise du message précédent)

Bonjour,

jb_gfx a écrit :

Juste un truc, quand je regarde ton code : tu te bases sur le nom du fichier pour déterminer son format (d'après l'extension). Cela pose plusieurs problèmes :

- Au niveau de la sécurité : l'utilisateur peut très bien changer le l'extension du fichier et tenter d'exploiter ton script pour le détourner de son fonctionnement initial.

...

Pour déterminer si un fichier est dans le bon format tu devrais te baser sur son type mime.


C'est pareil pour le mimetype. Comme c'est le navigateur Web qui transmet le mimetype du fichier, il peut être falsifié.

Je vais revenir un peu plus tard pour donner la procédure pour sécuriser au maximum l'upload de fichiers.
Modifié par Tony Monast (26 Apr 2011 - 23:23)
Modérateur
Bonsoir,

Voici les étapes pour avoir un bon niveau de sécurité lors de l'upload de fichiers, mais je vais partir du principe qu'on parle essentiellement d'images.

1. Si l'application exige d'être identifié à son compte, s'assurer que l'utilisateur ne peut pas uploader sans être identifié.
2. Valider le mimetype du fichier à partir d'une liste blanche.
3. Valider l'extension du fichier à partir d'une liste blanche.

Le point 2 et 3 peuvent être falsifiés par l'utilisateur. Il s'agit d'une protection complémentaire aux points suivants :

4. Limiter la taille et le nombre des fichiers qu'un utilisateur peut envoyer afin d'éviter qu'il ne consomme toutes les ressources et qu'il mettre le serveur à terre.
5. L'upload doit se faire de préférence dans un dossier non accessible par le Web.
6. Valider avec un script que le contenu du fichier est bel et bien une image. Il s'agit d'une protection complémentaire, car ce n'est pas infaillible.
7. Renommer le fichier avec un nom prédéterminé (membreid + datetime + random) ou ne conserver que certains caractères comme les lettres, les chiffres, le point, les tirets et les soulignements. Remplacer tous les autres caractères par des tirets par exemple.
8. Déplacer le fichier dans un dossier accessible par le Web. Ce dossier doit absolument être configuré pour n'exécuter aucun script côté serveur. C'est la protection la plus fiable, car même si un utilisateur arrivait à percer toutes les protections précédentes pour y placer un fichier PHP et l'atteindre via http, le serveur refuserait d'exécuter/interprété le dit fichier. Ça peut se faire dans la configuration du serveur ou directement dans un fichier htaccess.
9. Pour compléter le tout, ce n'est pas non plus une mauvaise idée de conserver une trace des activités : nom d'origine du fichier, ip, date et heure, taille, mimetype, membreid, etc. et d'y jeter un oeil à l'occasion.

J'ai trouvé un article en anglais qui est assez détaillé. C'est en PHP, et comme ce n'est pas mon langage de prédilection, je ne sais pas si le code fourni est 100% correct, mais au premier coup d'oeil ça m'avait l'air bien. Quelqu'un d'autre pourra confirmer.

Cela dit, le mieux reste quand même de faire quelques recherches sur Google pour voir l'ensemble des bonnes pratiques en matière d'upload de fichiers. Il y a beaucoup d'articles sur le sujet. Il faut seulement faire attention, car certains d'entre-eux sont incomplets ou incorrects.
a écrit :

C'est pareil pour le mimetype. Comme c'est le navigateur Web qui transmet le mimetype du fichier, il peut être falsifié.


Oui tu as raison par rapport à mon exemple précédent, il faut utiliser l'extension mime.magic avec FileInfo pour valider le type du fichier.

a écrit :

2. Valider le mimetype du fichier à partir d'une liste blanche.


Tout à fait, c'est ce que je disais dans mon post précédent. Smiley cligne

a écrit :

3. Valider l'extension du fichier à partir d'une liste blanche.


Non, comme je l'ai dit les fichiers n'ont pas toujours d'extension (c'est une particularité de Windows à la base). Sur un site grand public que je gère les utilisateurs peuvent télécharger des photos sur leurs profils : j'ai a peu près 5% d'images téléchargées qui n'ont pas d'extensions.

a écrit :

7. Renommer le fichier avec un nom prédéterminé (membreid + datetime + random) ou ne conserver que certains caractères comme les lettres, les chiffres, le point, les tirets et les soulignements. Remplacer tous les autres caractères par des tirets par exemple.


Je rajouterai que c'est à ce moment là que tu peux rajouter l'extension en la générant depuis le mime type.

a écrit :

8. Déplacer le fichier dans un dossier accessible par le Web. Ce dossier doit absolument être configuré pour n'exécuter aucun script côté serveur. C'est la protection la plus fiable, car même si un utilisateur arrivait à percer toutes les protections précédentes pour y placer un fichier PHP et l'atteindre via http, le serveur refuserait d'exécuter/interprété le dit fichier. Ça peut se faire dans la configuration du serveur ou directement dans un fichier htaccess.


Si tu as bien généré l'extension du fichier à l'étape précédente et que ton serveur est bien configuré, ce point est inutile.
Modifié par jb_gfx (27 Apr 2011 - 14:34)
Modérateur
jb_gfx a écrit :

Euh non pas du tout. Le mime type est déterminé côté serveur par PHP. Voir l'extension mime.magic et la fonction mime_content_type(). Par contre ça ne s'applique pas à mon exemple précédent (si on utilise le tableau $_FILES c'est bien la navigateur qui transmet le mime type). Edit: FileInfo dans les versions plus récentes de PHP.


Mon commentaire était relié au fait que tu avais donné en exemple le tableau $_FILES qui est bel et bien transmis par le navigateur, donc tu ne peux pas me répondre "Euh non pas du tout" comme si ce que je disais était faux. Smiley cligne .

jb_gfx a écrit :


"Ce dossier doit absolument être configuré pour n'exécuter aucun script côté serveur. C'est la protection la plus fiable, car même si un utilisateur arrivait à percer toutes les protections précédentes pour y placer un fichier PHP et l'atteindre via http, le serveur refuserait d'exécuter/interprété le dit fichier. Ça peut se faire dans la configuration du serveur ou directement dans un fichier htaccess."

Si tu as bien généré l'extension du fichier à l'étape précédente et que ton serveur est bien configuré, ce point est inutile.


Inutile? Ma foi, c'est cette protection la plus fiable et la plus sûre, parce que même si un utilisateur arrivait à faire passer un fichier php en contournant toutes les protections précédentes, aucun script ne pourrait être lancé. Si on veut se blinder, il faut mettre en place le maximum de couches de sécurité. Après, c'est à toi de voir quels risques tu es prêt à assumer. Pour ma part, pour les applications sensibles, je ne néglige aucun point.
Tony Monast a écrit :


Mon commentaire était relié au fait que tu avais donné en exemple le tableau $_FILES qui est bel et bien transmis par le navigateur, donc tu ne peux pas me répondre "Euh non pas du tout" comme si ce que je disais était faux. Smiley cligne .


Oui j'ai écris trop vite (comme d'hab). Smiley langue
J'ai édité mon message précédent.
Modérateur
Je t'invite aussi à regarder le Case 6 de cet article, qui démontre qu'il est primordial de désactiver les scripts pour le dossier. Techniquement, il est possible de mettre du code PHP dans le commentaire d'une image, et le fichier conserve une entête d'image valide. Alors en théorie, même si tu récupères le mimetype côté serveur, c'est insuffisant, car il peut être falsifié. J'ai même vu un exemple où il était possible de tromper le serveur avec l'extension du fichier en utilisant des caractères spéciaux dans le nom du fichier.

Pour une sécurité efficace, il faut donc combiner plusieurs solutions. On ne peut pas être trop prudent. Le fait de bloquer les scripts pour le dossier annule complètement les failles qui pourraient avoir dans les couches de sécurité précédentes.
Bonjour à tous et merci de toutes ces pistes !

Alors j'ai tenté de suivre (tant bien que mal) vos conseils.
Pour ce qui est de l'upload, je pense que c'est bon, par contre, pour le téléchargement, je continue de me casser les dents.

Donc pour reprendre, l'utilisateur se sert de checkbox pour choisir ses fichiers.
Ensuite, grâce à une commande DOS (j'ai découvert dans mes péripéties qu'on pouvait parler en php à windows... génial Smiley lol ) je créée dans un autre dossier un fichier texte contenant tous les fichiers choisis. Ma commande créée également une archive 7z dans ce même dossier(le fichier en question et son archive portent le nom de l'utilisateur et s'effaceront à chaque nouveau téléchargement de ce dernier).


$cible_save_progress='temp/'.$u_doss.'_archive.zip';
$cmd='7za a -t7z "'.$cible_save_progress.'" "@temp/'.$u_doss.'.txt"'." -mx=9";
echo $cmd;
exec($cmd, $output);


Donc jusque là tout va bien, sauf que mon archive contient toute mon arborescence des fichiers originaux. et vous comprendrez que c'est bien ennuyeux...
Voilou, si vous avez des idées pour résoudre ce dernier pépin, ou si vous voyez des erreurs je suis évidemment preneur (?? preneuse ??)...
C'est la dernière étape, après je mets en ligne... Smiley smile

En tous les cas vraiment je vous remercie de votre aide !!
et vous souhaite à tous un très bon week-end !

EDIT : tuout va bien qui se termine sans soucis, je suis finalement parvenu, bien tard, à faire ce que je voulais.
Aux intéressés peut être... voici :

//effacement des fichiers d'archive avant la boucle
unlink("temp/".$u_doss.".txt");	
unlink("temp/".$u_doss."_archive.7z");
unlink("Archive_".$u_doss.".7z");
//fonction qui efface dossier d'archive et son contenu
rrmdir("Archive_".$u_doss);
//boucle qui liste les fichiers téléchargés et qui va les chercher dans leur dossiers respectifs selon que ce soit des images watermarkées ou des fichiers autres
$cible_save_progress='temp/'.$u_doss.'_archive.7z';
$cible_ss_arbo='Archive_'.$u_doss.'.7z';
//$cmd créa 7zip + arbo
$cmd='7za a -t7z "'.$cible_save_progress.'" "@temp/'.$u_doss.'.txt"'." -mx=0";
exec($cmd);
//$cmd 7zip ss arbo créa fichier 
$cmd='7za e "temp/'.$u_doss.'_archive.7z" -o"Archive_'.$u_doss.'/" -y';
exec($cmd);
//téléchgmt 7zip ss arbo créa dossier
$cmd='7za a -t7z "'.$cible_ss_arbo.'" "Archive_'.$u_doss.'/*"'." -mx=0";
exec($cmd);				


(c'est un peu tiré par les cheveux, j'admets, mais j'arrive à mes fins...)
Merci encore à tous pour vos bonnes pistes de recherches qui ont permit de sécuriser un peu mon mini site.
Modifié par ClR (01 May 2011 - 06:21)
Pages :