Je veux juste que l'utilisateur puisse télécharger des fichiers .txt ou .csv.
Cela semble facile, n'est-ce pas? Ce n'est pas le cas. Et même plus.
L'approche simple consiste simplement à vérifier que le fichier se termine par ‘.txt’ ou ‘.csv’ avant de le stocker sur le système de fichiers. Cela devrait faire partie d'une validation beaucoup plus approfondie de ce que le nom de fichier est autorisé à contenir avant de laisser un nom de fichier soumis par l'utilisateur approcher le système de fichiers.
Parce que les règles concernant ce qui peut aller dans un nom de fichier sont complexes sur certaines plateformes (surtout sur Windows), il est généralement préférable de créer votre propre nom de fichier de manière indépendante avec un nom et une extension connue.
Quoi qu'il en soit, il n'y a aucune garantie que le navigateur vous enverra un fichier avec un nom utilisable, et même s'il le fait, il n'y a aucune garantie que ce nom contiendra ‘.txt’ ou ‘.csv’ à la fin, même s'il s'agit d'un fichier texte ou CSV. (Certaines plateformes n'utilisent simplement pas d'extensions pour le typage des fichiers.)
Alors que vous pouvez essayer de renifler le contenu du fichier pour voir de quel type il pourrait s'agir, cela est très peu fiable. Par exemple:
,,,
pourrait être du texte brut, du CSV, de l'HTML, du XML, ou une variété d'autres formats. Il est préférable de donner à l'utilisateur un contrôle explicite pour dire quel type de fichier il télécharge (ou d'utiliser un champ de téléchargement de fichier par type).
Maintenant, voici où cela devient vraiment difficile. Disons que vous avez accepté le téléchargement et l'avez stocké sous /data/mygoodfilename.txt, et que le serveur Web le sert correctement en tant que Content-Type ‘text/plain’. A quoi pensez-vous que le navigateur l'interprète? Du texte brut? Vous seriez bien chanceux.
Le problème est que les navigateurs (principalement IE) ne font pas confiance à votre en-tête Content-Type, et au lieu de cela reniflent le contenu du fichier pour voir s'il ressemble à autre chose. Servez le snippet ci-dessus en tant que texte brut, et IE le traitera joyeusement comme de l'HTML. Ceci peut être un énorme problème, car l'HTML peut inclure des scripts côté client qui prendront le contrôle de l'accès de l'utilisateur au site (une attaque de scriptage intersite).
À ce stade, vous pourriez être tenté de renifler le fichier côté serveur, par exemple en utilisant la commande ‘file’, pour vérifier s'il ne contient pas ‘’. Mais cela est voué à l'échec. La commande ‘file’ ne renifle pas tous les mêmes balises HTML qu'IE et les autres navigateurs reniflent de toute façon différemment. Il est assez facile de préparer un fichier que ‘file’ prétendra ne pas être de l'HTML, mais qu'IE traitera néanmoins comme tel (avec des implications de catastrophe en matière de sécurité).
Les approches de reniflement de contenu telles que ‘file’ ne vous apporteront qu'un faux sentiment de sécurité. C'est un outil de commodité pour deviner grossièrement les types de fichiers et pas une mesure de sécurité efficace.
À ce stade, vos dernières possibilités désespérées sont des choses comme:
-
servir tous les fichiers téléchargés par les utilisateurs à partir d'un nom d'hôte séparé, afin qu'une attaque par injection de script ne puisse pas voler les identifiants de votre site principal;
-
servir tous les fichiers téléchargés par les utilisateurs via un wrapper CGI, en ajoutant l'en-tête ‘Content-Disposition: attachment’ afin que les navigateurs ne tentent pas de les afficher directement;
-
accepter uniquement les téléchargements des utilisateurs de confiance.