Table des matières
A simple, minimalist, personal file/image hosting script
Presentation
I'm tired of image hosting services. Sites like imageshack.us are riddled with advertising. Sites like imgur.com delete images with no warning (Yes, it has happened to me several times). And some of them even want to sign-up for "premium" accounts ? WTF ?
I decided to create my own, minimalist image hosting script for my own website. In fact, this script can be used to upload & host any kind of file.
This is very simple.
- Only you can upload files (using the password)
- Anybody can see the images or download the files.
Instructions
- Save the following script as
upload.php
. - Change the password (
$PASSWORD='toto';
) - Put the file in the root of your website.
- Enjoy !
Files will be saved in the files
subdirectory. You can change the directory name in the source ($SUBDIR='files
').
Source
- upload.php
<html><head><style type="text/css"> <!-- body { font-family: "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif; font-size: 10pt; background-color: #eee;} --> </style> <?php // A simple, minimalist, personal file/image hosting script. - version 0.5 // Only you can upload a file or image, using the password ($PASSWORD). // Anyone can see the images or download the files. // Files are stored in a subdirectory (see $SUBDIR). // This script is public domain. // Source: http://sebsauvage.net/wiki/doku.php?id=php:imagehosting $PASSWORD='toto'; $SUBDIR='files'; // subdirectory where to store files and images. if (!is_dir($SUBDIR)) { mkdir($SUBDIR,0705); chmod($SUBDIR,0705); $h = fopen($SUBDIR.'/.htaccess', 'w') or die("Can't create .htaccess file."); fwrite($h,"Options -ExecCGI\nAddHandler cgi-script .php .pl .py .jsp .asp .htm .shtml .sh .cgi"); fclose($h); $h = fopen($SUBDIR.'/index.html', 'w') or die("Can't create index.html file."); fwrite($h,'<html><head><meta http-equiv="refresh" content="0;url='.$_SERVER["SCRIPT_NAME"].'"></head><body></body></html>'); fclose($h); } $scriptname = basename($_SERVER["SCRIPT_NAME"]); if (isset($_FILES['filetoupload']) && isset($_POST['password'])) { sleep(3); // Reduce brute-force attack effectiveness. if ($_POST['password']!=$PASSWORD) { print 'Wrong password.'; exit(); } $filename = $SUBDIR.'/'.basename( $_FILES['filetoupload']['name']); if (file_exists($filename)) { print 'This file already exists.'; exit(); } if(move_uploaded_file($_FILES['filetoupload']['tmp_name'], $filename)) { $serverport=''; if ($_SERVER["SERVER_PORT"]!='80') { $serverport=':'.$_SERVER["SERVER_PORT"]; } $fileurl='http://'.$_SERVER["SERVER_NAME"].$serverport.dirname($_SERVER["SCRIPT_NAME"]).'/'.$SUBDIR.'/'.basename($_FILES['filetoupload']['name']); echo 'The file/image was uploaded to <a href="'.$fileurl.'">'.$fileurl.'</a>'; } else { echo "There was an error uploading the file, please try again !"; } echo '<br><br><a href="'.$scriptname.'">Upload another file.</a>'; exit(); } print <<<EOD <form method="post" action="$scriptname" enctype="multipart/form-data"> File/image to upload: <input type="file" name="filetoupload" size="60"> <input type="hidden" name="MAX_FILE_SIZE" value="256000000"><br> Password: <input type="password" name="password"><br> <input type="submit" value="Send"> </form> <small>Self-hosting php script by <a href="http://sebsauvage.net/wiki/doku.php?id=php:filehosting">sebsauvage.net</a></small> EOD; ?> </body> </html>
Please note that you are limited by the php upload file size limit (see php.ini) and the maximum POST size accepted by Apache (see you apache configuration). You can use phpinfo()
to find this limit (Search for post_max_size
and upload_max_filesize
).
Version history
- 0.1 : First version.
- 0.2 : Moved php script out of the data directory. Added proper htaccess file for better security (prevent execution of uploaded files such as .php).
- 0.3 : Accessing the data directory now redirects to the upload script.
- 0.4 : Added a sleep() to reduce brute-force attack effectiveness.
- 0.5 : Path correction (thanks to Elouan Pignet)
Discussion
Merci, merci! J'ai un site depuis 1997! mais je ne sais pas programmer… Votre fichier est nickel et me facilitera la vie. PS: j'ai francisé les textes et si vous en souhaitez cette version, me la demander.
Salut ! Moi j'aimerais bien la version française :P
Bonjour,Je serais ravi d'avoir votre traduction “française ” du texte ci-dessus Je vous remercie pour votre collaboration Noel A.
Simple, clair et super utile, c'est comme ça que j'aime les scripts php ! Ceux qui veulent peuvent le modifier facilement pour l'adapter à leurs besoins.
Je viens de le mettre sur ma dedibox et je vais pouvoir me passer des imageshack & co pour publier mes photos sur EOS-numerique :)
Merci à toi ! :)
Salut,
un petit anti-force brute pourrait être nécessaire quand on voit l'efficacité de ces protections et la tendance des gens à mettre des mot de passes de plus en plus débiles ;)
Ok… j'ajoute ça. Un délai de 3 secondes devrait être suffisant. Si vous avez d'autres idées, n'hésitez pas à proposer.
Le sleep que tu as ajouté ralonge le temps de chargement de la page mais on peut faire plusieurs requêtes simultanément non ? Donc au bout du compte ça change rien (si je ne me trompe pas )
Perso j'aurais utilisé des sessions pour identifier qui a fait un essai récemment et limiter le nombre d'essais par minute mais bon c'est pas infaillible non plus: On peut facilement se faire passer pour quelqu'un d'autre (existe-t-il un moyen sûre d'identifier quelqu'un ?)
Une idée un peu tordue pour se battre contre la force brute : créer un fichier indiquant qu'une requête est en traitement au début du script et le supprimer à la fin (avec les 3 secondes). En rajoutant au début du fichier while (isset($file_anti_brute)) {sleep(2);} ca devrait serieusement ralentir l'attaquant.
Qu'en pensez-vous ?
Oui, je sais pour les requêtes simultanées. En effet la protection pourrait être meilleure. Mais je ne voulais pas aller jusqu'à une captcha.
Le sessions, ça ne marcherait pas: il suffirait que son client accepte les cookies (ça se programme très bien en python), et les jette à l'essai suivant.
L'IP pourrait à la limite marcher, du genre: Limiter le nombre d'envoi de formulaire par minute et par IP.
Hello Seb,
Toutes mes félicitations. En publiant le programme, cela rendra service à d'autres. No comment: comme d'hab'! Bonne continuation.
Ps: pense à checker tes MP sur CCM quand t'auras un peu de temps ;)
Aucun contrôle sur le fichier envoyé ? (MIME) … was uploaded to ($fileurl - possible XSS ?) Pour éviter les doublons, possible de préfixer (timestamp).
Aucun contrôle MIME sur le fichier envoyé. C'est le serveur web qui déterminera le type MIME à servir au moment du téléchargement.
Pour le $fileurl, tu as raison, il n'est pas correctement échappé, mais bon celui qui obtient cette page connaît le mode de passe. Le propriétaire du site ne devrait pas essayer de faire une attaque XSS sur son propre site (et puis le formulaire est en POST).
”Pour éviter les doublons, possible de préfixer (timestamp).”
Oui je pourrais générer un identifiant unique, de manière à pouvoir uploader des fichiers de même nom, mais j'ai préféré garder le nom: Cela permet à celui qui upload de choisir des noms explicites.
Salut,
Tu peux simplement mettre une condition pour vérifier si le fichier existe déjà sous ce nom ; et si c'est le cas, tu rajoutes un préfixe .
Sinon je n'ai pas compris à quoi servait le .htaccess dans le répertoire files ?
Le .htaccess sert à empêcher l'exécution de scripts dans le répertoires files. Afin d'éviter que quelqu'un puisse uploader un .php et l'exécuter.
Tu pourrais aussi ajouter un champ dans le formulaire permettant de choisir quel sera le nom du fichier enregistré.
oui… ou non. On peut aussi renommer le fichier avant de l'uploader. Je ne pense pas que ça soit plus lourd. En tous cas, mon but était de garder le script le plus minimaliste possible, donc de limiter la complexité du formulaire.
Juste un petit message pour te remercier pour ce petit script simple et efficace. Le top ! Longue vie à ton site.
Sébastien,
Merci pour ce petit script simple, court et efficace. Il fait ce qu'il doit faire et rien d'autre. Si tu en as d'autre du genre, je suis preneur.
Bonjour, juste un petit lien vers un script similaire quoiqu'un peu plus complet que je viens de découvrir. Il n'y a pas par défaut de système de mot de passe empêchant l'upload par des tiers mais c'est facilement rajoutable. Cela peut peut-être servir à d'autres :) http://projets.kd2.org/p/fotoo-hosting/
Oui je connais la galerie de BohwaZ, il m'en avait parlé. Je cherchais seulement à faire minimaliste et fonctionel
salut Sébastien
merci pour tes lumières qui illuminent ma newbbie connaissance informatique.
Combien tu paries que certains laisseront 'toto' en mot de passe ? :D
Bonjour,
Super script, je me suis permis d'ajouter une authentification par htaccess par login et mdp, et une fonction qui détecte si le fichier est une image, et qui en crée une miniature pour l'affichage sur les forums phpBB, ainsi qu'une page qui permet de lister les fichier présents sur le serveur.
Je suis en train de rajouter un back office, pour ajouter un utilisateur dans le htaccess (avec le mdp en crypté of course).
Bonne soirée ;)
oh… woao. Chouette.
bonjour, je ne sais pas si tu passe encore ici mais si jamais tu peux partager ta modif ça me sera très utile. merci
Je ne fais que passer pour tester la tête qu'à mon vizhash sur ton wiki. Si il est trop moche, j'ouvre un rapport de bug :)
ça va je l'aime bien !
Pourquoi ne pas mettre le sleep(3) dans le if juste en dessous, comme ça :
if ($_POST['password']!=$PASSWORD) { print 'Wrong password.';sleep(3); exit(); }
Il aurait la même utilité anti-bruteforce, mais ne gênerait pas un utilisateur normal (qui connaitrait donc le mot de passe).Super ! Un grand merci pour ce script simple et efficace ! Je tournais autour de la question depuis longtemps, et je serais prêt à faire une petite donation
Par contre, dans l'idéal pour mes besoins, je souhaiterais avoir un effacement “automatique” des fichiers au bout d'un certain temps. Plusieurs stratégies me conviendraient : * Idéal : spécifier un “time-to-live” au moment du dépôt * Bien : délai par défaut réglable par une constante du programme Dans mon cas, la gestion des effacements (cleanup) lors d'une opération d'upload me suffit.
Dans tous les cas Bravo d'avoir partagé votre script ! Cordialement, Bertrand
Pas mal de détails à voir
MAX_FILE_SIZE
doit se trouver avant le champfile
$_FILES['filetoupload']['error']
et pas seulement se fier à la limite duphp.ini
<head>
et l'ouverture de<body>
$_FILES['filetoupload']['error']
$fileurl
peut ête simplifié en$fileurl = $_SERVER['REQUEST_SCHEME'] . ':/
/' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . '/' . $filename;
rendant$serverport
inutile. Le port est indiqué dans$_SERVER['HTTP_HOST']
s'il est différent de 80mkdir
à un comportement imprévisible et est inutile vu qu'on fait unchmod
juste après$_SERVER['SERVER_SOFTWARE']
pour ne pas créer un .htaccess sur un serveur web non ApacheDans mon troisième point, je voulais dire
$_FILES['filetoupload']['size']
Super pratique, merci !
Il manque juste une option pour remplacer un fichier deja existant et ce serait parfait !
Bonjour, j'ai trouvé ce petit snippet pour aider à la protection contre les attaques en parallèle :
session_start()
if(isset($_SESSION['ip']) && $_SESSION['last_post'] + MININTERVAL < time()) die('too early');
$_SESSION['last_post'] = time();
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
store the message
Je ne parviens pas à modifier le script pour y ajouter moi-même une valeur incrémentée ou un timestamp dans le cas d'un upload en doublon. Un peu d'aide serait-elle possible ? (Je ne trouve pas cela non plus sur les forums, ne sachant pas quoi chercher exactement)
J'ai changé le filename sur upload.php pour y ajouter le timestamp avant le nom du fichier :
Ainsi
exemple.png
deviendra1480288380-exemple.png
:)Modifications.
Coucou, j'ai mis à jour le script de manière sommaire histoire d'avoir quelque chose de minimaliste et de pratique :)
Donc sur ma version ya un cookie de connexion qui nous évite de devoir saisir le mdp trop de fois en pas longtemps, les fichiers ont le timestamp rajouté en début de nom, et j'ai rajouté une page listant les fichiers si on est connecté (marche avec un mdp différent de upload.php, avec possibilité de supprimer les fichiers en un clic)
Screenshot 1 - Screenshot 2
Pour l'instant j'ai fait ça de manière très sale, mais je pense que je vais le re-coder plus proprement et ensuite mettre un lien contenant le code d'upload.php sur cette page :)
edit 6 mois plus tard: tadaaaah !
J'ai ajouté la possibilité de coller une image directement depuis le presse-papier (après un impr écran par exemple), et j'ai décidé d'héberger ça sur gitlab :)
@sodimel,
Il y a la variable SUBDIR mais ensuite dans le code files est utilisé en dur, pas la variable SUBDIR.
Merci Cristophe, j'ai réglé le souci :)
Bonjour, J'ai installé votre script php sur mon serveur. L'adresse de base inclut le https (port 443) et lorsque je termine de téléverser le fichier, le script me renvoie l'adresse avec une spécification de port tel http://www.monserveur.com:443/files/monfichierteleverse.txt. Y a-t-il moyen d'éviter le :443 et plutôt retourner une adresse https? Merci, Norm
Bonjour,
j'utilise votre excelent script,il semblerait qu'il ne gére pas les accents dans les fichiers , car sur mon serveur ftp je me retrouve avec un fichier sans nom ?
Merci de votre aide.
Bonjour.
La discussion est désormais close sur cette page.