PDA

Bekijk Volledige Versie : Images uploaden: invoer validatie



Jan Reilink
19/07/05, 20:55
Graag zou ik willen weten in hoeverre de volgende code "fool-proof" is?

----- begin (tabs verwijderd)
<?php
/*
* uploadform.php v 2.4 2005/07/19 16:48
*
*/

// Let er op dat er een "trailing slash" '/' achter het PATH
// geplaatst wordt. Ja, het is een IIS machine
$UploadDir = 'D:/www/example.org/www/upload/';
// Geef de naam in dat gebruikt wordt in het invoer veld
$UserFileName = 'userfile';

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>PHP File-Upload</title>
<meta http-equiv="content-type" content="text/html;charset=ISO-8859-1" />
</head>

<body>

<?PHP
// controleer of het formulier gesubmit is
if (isset($_POST['set'])) {

// kijk naar deze bestands typen (jpg|jpeg|gif|png|bmp)...
// Alleen dezen worden geaccepteerd.
if (eregi("(jpg|jpeg|gif|png|bmp)$",$_FILES[$UserFileName]['name'])) {

// controleert aan de hand van het mime-type of het wel
// daadwerkelijk een image betreft.
$MimeType =
image_type_to_mime_type(getimagesize($_FILES[$UserFileName]['tmp_name']));
if ($MimeType == ("image/png") || $MimeType == ("image/jpeg") ||
$MimeType == ("image/gif") || $MimeType == ("image/psd") || $MimeType ==
("image/bmp")) {

// eindigt de bestandsnaam met iets dat wij willen *en* is het
// mime-type goed bevonden, dan gaan we het bestand verplaatsen
// van de temp_directory naar $UploadDir
if (move_uploaded_file($_FILES[$UserFileName]['tmp_name'], $UploadDir .
$_FILES[$UserFileName]['name'])) {

// Geef een bevestiging aan de gebruiker
echo "<div>Bestand is goed bevonden en was succesvol geupload.</div>";
echo "<div>Bestandsnaam is: " .$_FILES[$UserFileName]['name'] . "<br>";
echo "Klik <a href='upload.php'>hier</a> om nog een bestand te
uploaden.</div>";
}
}
else {
// fout afhandeling mime-type
echo "Mimetype was niet goed";
}
}
else {
// Er is iets mis gegaan, het bestand voldoet niet aan wat wij willen.
// Geef een foutmelding weer, inclusief bestandsnaam.
echo "Error, filename was " . $_FILES[$UserFileName]['name'] . "<br>";
echo "Gebruik de <i>back button</i> om het nogmaals te proberen";
}
}
else {
// formulier was niet gesubmit, geef het formulier weer
?>

<div>
<form enctype="multipart/form-data" method="post" action="<?php echo
$_SERVER['PHP_SELF'];?>" id="upload">
<div>
<!-- De MAX_FILE_SIZE is een advies aan de browser, geen harde limiet -->
<input type="hidden" name="MAX_FILE_SIZE" value="2000000" />
Send this file: <input name="userfile" type="file" />
<input type="submit" name="set" value="Send File" />
</div>
</form>
</div>
<?php
}
?>

</body>
</html>

----- eind

Met mijn gelimiteerde kennis van PHP kon ik niet echt een andere manier
bedenken om te controleren of het geuploade bestand echt een image is,
dan het gebruik van de functies image_type_to_mime_type() en getimagesize().
Ik zou dus niet willen dat iemand deze functie (of de eregi op het einde
van de bestandsnaam) zou kunnen omzeilen om zodoende scripts te kunnen
uploaden.

Alvast bedankt voor enig input.

Groeten, Jan Reilink

Ronald Klip
20/07/05, 09:55
Jan Reilink schreef:
> Graag zou ik willen weten in hoeverre de volgende code "fool-proof" is?
>
> <?PHP
> // controleert aan de hand van het mime-type of het wel
> // daadwerkelijk een image betreft.
> $MimeType =
> image_type_to_mime_type(getimagesize($_FILES[$UserFileName]['tmp_name']));
> if ($MimeType == ("image/png") || $MimeType == ("image/jpeg") ||
> $MimeType == ("image/gif") || $MimeType == ("image/psd") || $MimeType ==
> ("image/bmp")) {

exif_imagetype() is volgens de manual sneller dan getimagesize(). Maar
dan dient PHP wel exif-enabled gecompileerd te zijn.

In plaats van al die || zou je in_array(naald, hooiberg) kunnen
gebruiken. Geen idee of het sneller is, maar het ziet er netter uit ;)

> // eindigt de bestandsnaam met iets dat wij willen *en* is het
> // mime-type goed bevonden, dan gaan we het bestand verplaatsen
> // van de temp_directory naar $UploadDir
> if (move_uploaded_file($_FILES[$UserFileName]['tmp_name'], $UploadDir .
> $_FILES[$UserFileName]['name'])) {

Volgens http://nl3.php.net/manual/nl/features.file-upload.php is dat de
juiste methode.
Je zou ook nog kunnen controleren op een maximale bestandsgrootte, als
die kleiner is dan wat de server-instellingen toelaten.

--
groet, Ronald

Daniel Tryba
20/07/05, 13:45
Jan Reilink <janreilink@vevida.nl> wrote:
> Graag zou ik willen weten in hoeverre de volgende code "fool-proof" is?
[upload script]

Bij een "openbaar" upload script moet je proberen:
-te voorkomen dat de inhoud serverside wordt uitgevoerd
-de inhoud overeenkomt met de opgegeven kenmerken
-de inhoud geen gevaar is voor die client

Het eerste punt handel je af met een regexp (waar de '.' voor extensie
mist)

Voor het 2de gebruik je getimagesize, hier ga je dus serverside iets
doen met de data, dat betekent dat je potentieel slachtfoffer bent van
bugs als
http://www.idefense.com/application/poi/display?id=222&type=vulnerabilities
(maw houdt de bugs/updates bij)

Het 3de punt is natuurlijk alleen van toepassing indien de geuploaded
files weer worden gebruikt in bv een browser, er is AFAIK geen browser
die nog nooit last heeft gehad fouten in hen image afhandeling, bv
http://www.frsirt.com/english/advisories/2005/0296 of
http://www.microsoft.com/technet/security/Bulletin/MS05-025.mspx
Je zou dit kunnen proberen te voorkomen door het image serverside te
transformeren (maar dan ben je uiteraard weer vatbaar voor 2).

Je script lijkt me dan ook een goede poging om bovenstaande te voorkomen
(met de beschikbare info :)

Jan Reilink
20/07/05, 15:15
On 2005-07-20, Daniel Tryba <partmapsswen@invalid.tryba.nl> wrote:
> Jan Reilink <janreilink@vevida.nl> wrote:
>> Graag zou ik willen weten in hoeverre de volgende code "fool-proof" is?
> [upload script]
>
> Bij een "openbaar" upload script moet je proberen:
> -te voorkomen dat de inhoud serverside wordt uitgevoerd
> -de inhoud overeenkomt met de opgegeven kenmerken
> -de inhoud geen gevaar is voor die client
>
> Het eerste punt handel je af met een regexp (waar de '.' voor extensie
> mist)
>

De regexp controleert de laatste 3 (of 4) characters van de bestandsnaam,
over het algemeen de extensie. Mijns inziens is door de '$' in de regexp
het gebruik van een '.' voor de extensie overbodig. Toch?

> Voor het 2de gebruik je getimagesize, hier ga je dus serverside iets
> doen met de data, dat betekent dat je potentieel slachtfoffer bent van
> bugs als
> http://www.idefense.com/application/poi/display?id=222&type=vulnerabilities
> (maw houdt de bugs/updates bij)
>

Hier had ik inderdaad niet bij nagedacht, dank.

--
Met vriendelijke groet / Best regards,
Jan Reilink, VEVIDA Nederland BV

Postbus 329, 9700 AH GRONINGEN, +31(0)50 - 5492234

Jan Reilink
20/07/05, 15:25
On 2005-07-20, Ronald Klip <nieuws@bladibla.net.invalid> wrote:
> Jan Reilink schreef:
>> Graag zou ik willen weten in hoeverre de volgende code "fool-proof" is?
>>
>> <?PHP
>> // controleert aan de hand van het mime-type of het wel
>> // daadwerkelijk een image betreft.
>> $MimeType =
>> image_type_to_mime_type(getimagesize($_FILES[$UserFileName]['tmp_name']));
>> if ($MimeType == ("image/png") || $MimeType == ("image/jpeg") ||
>> $MimeType == ("image/gif") || $MimeType == ("image/psd") || $MimeType ==
>> ("image/bmp")) {
>
> exif_imagetype() is volgens de manual sneller dan getimagesize(). Maar
> dan dient PHP wel exif-enabled gecompileerd te zijn.
>

Dank, hier zal ik zeker eens naar kijken.

> In plaats van al die || zou je in_array(naald, hooiberg) kunnen
> gebruiken. Geen idee of het sneller is, maar het ziet er netter uit ;)
>

Het gebruik van (een) array(s) staat al op het todo lijstje, helemaal
als de controle lijst uitgebreid moet worden. Maar eens in verdiepen :)

>> // eindigt de bestandsnaam met iets dat wij willen *en* is het
>> // mime-type goed bevonden, dan gaan we het bestand verplaatsen
>> // van de temp_directory naar $UploadDir
>> if (move_uploaded_file($_FILES[$UserFileName]['tmp_name'], $UploadDir .
>> $_FILES[$UserFileName]['name'])) {
>
> Volgens http://nl3.php.net/manual/nl/features.file-upload.php is dat de
> juiste methode.
> Je zou ook nog kunnen controleren op een maximale bestandsgrootte, als
> die kleiner is dan wat de server-instellingen toelaten.
>

Ook een leuke feature / test.

--
Met vriendelijke groet / Best regards,
Jan Reilink, VEVIDA Nederland BV

Postbus 329, 9700 AH GRONINGEN, +31(0)50 - 5492234