/    Sign up×
Community /Pin to ProfileBookmark

Restricting File Types In Upload

I’m trying to limit what users can and can’t upload on my site, based on MIME file types, detected by the browser, so the user can’t change an extention and pass malicious code to my very, very small, humble site.

Despite my efforts as a beginner, it’s just not working. Depending on what I change, it’ll either allow EVERYTHING or allow NOTHING. URGH!

So, I’m asking for help. Below is the function, and below that is the whole script it’s sitting in

[code=php]if ($xxxx_xxxx_xxxx != “application/pdf” || “audio/mpeg” || “audio/x-mpegurl” || “audio/x-wav” || “image/bmp” || “image/gif” || “image/jpeg” || “image/tiff” || “video/mpeg” || “video/quicktime” || “video/x-la-asf” || “video/x-ms-asf” || “video/x-msvideo”)

{

echo “YOUR FILE TYPE IS NOT ALLOWED<br />”;

$ok=0;

}

else

{

echo “YOUR FILE TYPE IS APPROVED<br />YOU WILL BE REDIRECTED IN 5 SECONDS”;

$ok=1;

}[/code]

. . . and the whole thing it sits in . . .

[code=php]<?php

$target = “./”;

$target = $target . basename( $_FILES[‘uploaded’][‘name’]) ;

$ok=1;

//This is our size condition

if ($uploaded_size > 262144000)

{

echo “Your file MUST be smaller than 250MB’s.<br>”;

$ok=0;

}

//This is our limit file type condition

if ($xxxx_xxxx_xxxx != “application/pdf” || “audio/mpeg” || “audio/x-mpegurl” || “audio/x-wav” || “image/bmp” || “image/gif” || “image/jpeg” || “image/tiff” || “video/mpeg” || “video/quicktime” || “video/x-la-asf” || “video/x-ms-asf” || “video/x-msvideo”)

{

echo “YOUR FILE TYPE IS NOT ALLOWED<br />”;

$ok=0;

}

else

{

echo “YOUR FILE TYPE IS APPROVED<br />YOU WILL BE REDIRECTED IN 5 SECONDS”;

$ok=1;

}

//Here we check that $ok was not set to 0 by an error

if ($ok==0)

{

Echo “Sorry your file was not uploaded”;

}

//If everything is ok we try to upload it

else

{

if(move_uploaded_file($_FILES[‘uploaded’][‘tmp_name’], $target))

{

echo “The file “. basename( $_FILES[‘uploadedfile’][‘name’]). ” has been uploaded”;

}

else

{

echo “Sorry, there was a problem uploading your file.”;

}

}

?>[/code]

So, any help on what I’m supposed to be using, would be GREAT. As a side note, I’ve tried [ $uploaded_type ] [ $mime_content_type ] [ $mime_type ] and it’s not working.

to post a comment
PHP

15 Comments(s)

Copy linkTweet thisAlerts:
@SyCoDec 02.2008 — [code=php]if ($xxxx_xxxx_xxxx != "application/pdf" ||...[/code]
should work but $xxxx_xxxx_xxxx isn't actually the thing you're comparing then I guess we would have to hazard a guess at what is going on.

So I presume that $xxxx_xxxx_xxxx is really $_FILES['uploaded']['type'] although the fact youve xxxed it out might mean its not? Who knows?

I'm not sure why you're doing this

[code=php] $target = "./";
$target = $target . basename( $_FILES['uploaded']['name']) ;[/code]

why not this
[code=php] $target = "./" . basename( $_FILES['uploaded']['name']) ;[/code]

Your logic is a little flawed too. You're setting OK to 1 by default. For basic security you should reject every file that does not meet a condition not the other way round eg

[code=php]//dont set $ok at all
if(filecheck($_FILES)){
//process uploaded file
}else{
//reject
}
[/code]


and have the filecheck() function do all the file checking and only returning true if the file is OK, then no need for a flag. If required it could return one or set one in the conditional.

I think it's a good idea to use a white list like you have with your array. You might want to put it in a function so if you need to upload anywhere else int he future you only have one white list to maintain. any additional file checking is also done in one place.

I see little point showing a message for success then relocating, I personally don't like being held up for no reason on a website. I would just move the user on to the new page on sucess and show a message if necessary at the landing page.
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 02.2008 — Despite my efforts as a beginner[/QUOTE]
As a side note, I've tried [ $uploaded_type ] [ $mime_content_type ] [ $mime_type ] and it's not working. [/QUOTE]

I'm sorry my "Logic" was flawed, by the industry standard. As I said, I'm a beginner & I read a bunch of "How-To" sites, and tried to make my own.

I appreciate your help, what you were able to give, but I didn't really understand most of what you said. Perhaps you're able to break it down a little for me?

I'm trying to learn this, but it's seeming to be an uphill battle.
Copy linkTweet thisAlerts:
@NogDogDec 02.2008 — If you are using the $_FILES[...]['type'] value to determine the file type, note that this is very undependable. It is not required that the browser sends it, you have no way to be sure that the browser has correctly identified it even if it sends it, and a hacker can easily spoof it.

You could try something like the [url=http://php.net/manual/en/intro.fileinfo.php]FileInfo PECL extension[/url], though even it only claims:
The functions in this module try to guess the content type and encoding of a file by looking for certain magic byte sequences at specific positions within the file. While this is not a bullet proof approach the heuristics used do a very good job. [/quote]
Copy linkTweet thisAlerts:
@SyCoDec 02.2008 — Industry standard? What industry standard? Do you mean the PHP.net website?

Setting a flag to true by default, then setting it false under certain conditions is not the right way to consider an issue of security. I doubt you will find anyone to disagree around here. It is less secure then setting it to false (or not setting anything) and setting it to true under certain conditions. If you learned the way you have done this on the PHP.net website then please post the URL of the page and I will add a comment to the manual.

Programming languages change over time. If you're learning this from a message posted last year it's likely out of date. It's also possible the person posting it is wrong.

Logic is not something to put in inverted commas. It's the essence of programming, logic and the syntax that allows that logic to become tangible.

You are not posting relevant code. Without relevant code we can't help you. This means nothing.
[code=php]
[ $uploaded_type ] [ $mime_content_type ] [ $mime_type ] [/code]


Nor does this
[code=php]$xxxx_xxxx_xxxx[/code]

These are variables and could be
[code=php]$mime_type='apple';[/code]
for all we know. You say you used them, but how?

I asked a lot of question in my last post. You've not answered any. Maybe you have good reason to do the things you've done? I don't know, all I can see is what you've posted. It doesn't make sense to me. If you have good reason then please explain so we can help you, if you don't have good reason and it's a noob thing then please just say, no one cares you're a beginner.

What exactly do you not understand. I'm not going to reiterate every point if you only are having trouble on 1 or 2 things. Please be specific in your questions.

Please post relevant code. What are the variables you are using? Where are they coming from? Don't post a chunk of pseudo code (ie the string of xxxes for a var name) and ask for people to find the problems in it.

You reply seems like you're offended by my post, sorry if that's the case. By quoting yourself and talking about "industry standards", whatever they are, your post seems a little arrogant, not the ideal attitude if you've come here looking for help.

While no-one round here cares you're a beginner, we do care about giving our time and feeling like it's not being appreciated.
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 03.2008 — Okay, you're right, I was offended. I was put off more by the fact that a professional was scoffing at the modest attempts of a beginner. This was simply not the case, and I was wrong. I do appreciate the help you were trying to give, and I now better understand why & where my post was flawed. As to where the code was garnered from, you can find that site HERE [about.com]. I tried to incorporate some of the little knowledge I had on PHP & it all started to break down. I scowered PHP.net, for help, but then came here as it's always treated me right.

I have a book, "PHP & MySQL" by Larry Ullman, it has a section on Uploading, I'm going to see what I can get from there and start my uploading script over again, WITHOUT the help of "About.com" LOL.

The part that I don't see in the book & caused me so much pain in the beginning was how to (correctly) prevent certain files from being uploaded. I had used, in the "About.com" examples, "mime_file_type" & others in the place of the xxxed content, so what followed that is where I was trying to limit content.

So, I'm sorry to you, if I offended your kindness in helping me period, it was appreciated, it was helpful, and it was nice. Thank-you.?

I'll make a post in a day or so, and let you know how the NEW script turned out.
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 03.2008 — Okay, so I found a really nice uploading script HERE, and I read through it. I tried to add my own stuff for validating the content, I just want to make sure it's all okay. Here's the HTML form part:
[code=html]
<form enctype="multipart/form-data" action="upload.php" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="262144000" />
Choose a file to upload: <input name="uploaded_file" type="file" />
<input type="submit" value="Upload" />
</form>
[/code]


and here's the new PHP Uploading script, with my line for validating content:
[code=php]
<?php
//&#1057;heck that we have a file
if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0)) {
//Check if the file is accepted and it's size is less than 250MB's
$filename = basename($_FILES['uploaded_file']['name']);
$ext = substr($filename, strrpos($filename, '.') + 1);
if (($ext == "pdf" || "mp3" || "m3u" || "wav" || "bmp" || "gif" || "jpg" || "mpg" || "mov" || "avi" || "divx") && ($_FILES["uploaded_file"]["type"] == "application/pdf" || "audio/mpeg" || "audio/x-mpegurl" || "audio/x-wav" || "image/bmp" || "image/gif" || "image/jpeg" || "video/mpeg" || "video/quicktime" || "video/x-msvideo") &&
($_FILES["uploaded_file"]["size"] < 262144000)) {
//Determine the path to which we want to save this file
$newname = dirname(__FILE__).'/upload/'.$filename;
//Check if the file with the same name is already exists on the server
if (!file_exists($newname)) {
//Attempt to move the uploaded file to it's new place
if ((move_uploaded_file($_FILES['uploaded_file']['tmp_name'],$newname))) {
echo "It's done! The file has been saved as: ".$newname;
} else {
echo "Error: A problem occurred during file upload!";
}
} else {
echo "Error: File ".$_FILES["uploaded_file"]["name"]." already exists";
}
} else {
echo "Error: Your file is not an accepted type, or is too large (250MB's or smaller). Please contact the site admin for help.";
}
} else {
echo "Error: No file uploaded";
}
?>
[/code]


The only thing I can't figure out, is how do I send them back to the original page, so they can see what they've uploaded & upload another if they want? Also, how to you implement a status bar, so they know the browser hasn't frozen?

Thanks!?
Copy linkTweet thisAlerts:
@SyCoDec 03.2008 — No worries alex, it's all cool with me. I appreciate your reply.

About.com huh, well that explains that ? Lots of great advice posted there, lots of junk too.

I didn't look too deep into this but I think this script has a security hole. As nogdog said the mime type can easily be spoofed so is not reliable. This leaves just the check of the extension so a stripped down test version looks like this

[code=php]$filename="test.mp3";
$ext = substr($filename, strrpos($filename, '.') + 1);
if($ext == "pdf" || "mp3" || "m3u" || "wav" || "bmp" || "gif" || "jpg" || "mpg" || "mov" || "avi" || "divx") {
echo $filename;
}[/code]


The result is [B]test.mp3[/B] is seen as a valid file type. Unfortunately so is the file name [B]test.mp3.php[/B]

If I can upload a script with
[code=php]<?
exec($_GET['my_code']);
?>[/code]

Then you've been pwned pretty hard. I can execute any command I like, eg the dreaded rm -rf * (never run this code!!!) and delete your entire website. I've never tried but I bet I could wget my website into it's place too. The exec code could even be hidden in the image comments in an normal looking image.

A safer way to check the extension would be to use a regular expression
[code=php]foreach($ext as $v){
if(preg_match("/$v$/",$_FILES['userfile']['name'])){ [/code]


This loops through the content of the allowed extension whitelist array and checks the extension is at the end of the file name so test.mp3.php would fail.

To solve the returning issue just set the action of the upload form to itself (same page its on) and put the upload handling code on the same page. You don't need to come back because you never left.

You'll have to google for the status bars, there's lots of solutions most involve using a CGI perl script to poll the server. You should be able to find a complete solution.
Copy linkTweet thisAlerts:
@SyCoDec 03.2008 — I forgot to say, a major security feature is to put any user uploaded files in a non web browseable folder. Then use a script to call the file from its location.
Copy linkTweet thisAlerts:
@NogDogDec 03.2008 — I like to use pathinfo() to get the extension:
[code=php]
$allowed = array('jpeg', 'jpg', 'gif', 'png'); // use all lower-case here
$ext = strtolower(pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION));
if(in_array($ext, $allowed))
{
// OK, continue processing
}
else
{
// invalid file extension, return error
}
[/code]
Copy linkTweet thisAlerts:
@SyCoDec 03.2008 — Another thing to consider is exceeding the max file size. This check
[code=php]if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0)) { [/code]
is fine until the max file size in your php.ini is exceeded. Then the FILES array is simply empty no errors or anything. The POST info will still submit so the only way you know the form was submitted is to test for something in the POST array.

The value sent with the form can also be spoofed so again is not reliable. You can set it in your php.ini or locallaly in your script with ini_set(). Include a hidden input in your form called anything with any value and test for it to see if the form was posted.
Copy linkTweet thisAlerts:
@SyCoDec 03.2008 — Edit to above post: My mistake, you [B]cant use ini_set() [/B] for this as the file has already loaded by the time the local flag is called. You need to edit the actual ini file or set the flags in a .htaccess file.
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 07.2008 — Okay, first, let me apologize for the absence. I'm deployed to Iraq right now, and is internet is *cough* less than reliable. So, I've read your posts, and have incorporated them into the code I'm trying to write. I want to show you HOW I've incorporated it, and try to explain WHY it is the way it is. I don't want to just cut and paste I really want to learn WHY I'm doing what I'm doing so I can do it by myself in the future. Okay, here's the NEW code with "NogDog" example in it.

[code=php]
<?php
###########################################
# #
# This script's development was aided #
# significantly by "SyCo" & "NogDog" #
# of "Webdeveloper.com" // Many thanks. #
# script admin : [email protected] #
# #
###########################################
//&#1057;heck that we have a file
if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0)) {
//Check if the file meets our exceptions and it's size is less than 250MB's
$filename = basename($_FILES['uploaded_file']['name']);
$allowed = array('pdf', 'mp3', 'm3u', 'wav', 'bmp', 'jpeg', 'jpg', 'gif', 'png', 'mpg', 'mpeg', 'mov', 'avi', 'divx');
$ext = strtolower(pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION));
if(in_array($ext, $allowed)) && ($_FILES["uploaded_file"]["size"] < 262144000)) {
//Determine the path to which we want to save this file
$newname = dirname(__FILE__).'/upload/'.$filename;
//Check if the file with the same name is already exists on the server
if (!file_exists($newname)) {
//Attempt to move the uploaded file to it's new place
if ((move_uploaded_file($_FILES['uploaded_file']['tmp_name'],$newname))) {
echo "It's done! The file has been saved as: ".$newname;
} else {
echo "Error: A problem occurred during file upload!";
}
} else {
echo "Error: File ".$_FILES["uploaded_file"]["name"]." already exists";
}
} else {
echo "Error: Your file is not an accepted type, or is too large (250MB's or smaller). Please contact the site admin for help.";
}
} else {
echo "Error: No file uploaded";
}
?>
[/code]


Okay, so using "NogDog" example, I'm checking the extension itself instead of the data therein, because not only is the data not always sent, somebody could craft malicious code for the purpose of spoofing that check, but if you have a malicious script (.php) or some other kind of nefarious code (.js // .exe) it's not going to pass, because uploading virus.mp3 isn't going to do you much good when it comes time to execute the code. Right?

Taking the hint from "SyCo" my first check of whether there are any errors, from my form data regarding the size of the file:
[code=php]
if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0))
[/code]


is okay, until it is physically limited by my "php.ini" file. So, what I did, was set that value HIGHER than what my script was preventing, so as to not send anything empty to the browser:
[CODE]
; Maximum size of POST data that PHP will accept.
post_max_size = 300M

///////

;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

; Whether to allow HTTP file uploads.
file_uploads = On

; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
;upload_tmp_dir =

; Maximum allowed size for uploaded files.
upload_max_filesize = 300M
[/CODE]


i truly appreciate your help in getting me along with my put-put engine that is PHP . . . LOL. I not only hope that I have implemented your suggestions the right way, I hope that I have correctly stated WHY we're doing these things the right way as well. ?

Cheers,
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 08.2008 — I'm getting this error & I don't know why.


Parse error: syntax error, unexpected T_BOOLEAN_AND in /home/USERNAME/public_html/SUBDOMAIN/upload.php on line 16
[/QUOTE]


Here is line 16 for me:

[code=php]
line: 15 $ext = strtolower(pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION));
line: 16 if(in_array($ext, $allowed)) && ($_FILES["uploaded_file"]["size"] < 262144000)) {
line: 17 //Determine the path to which we want to save this file
[/code]


I'm THINKING the problem is with the "&&" thing, perhaps the first part of the "if" statement needs to be stopped with a semi-colon(? . . . I don't know.

Here is where it sits in my script, so you can get a better idea of what I'm doing.
[code=php]

<?php

###########################################
# #
# This script's development was aided #
# significantly by "SyCo" & "NogDog" #
# of "Webdeveloper.com" // Many thanks. #
# script admin : [email protected] #
# #
###########################################

//&#1057;heck that we have a file

if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0)) {

//Check if the file meets our exceptions and it's size is less than 250MB's

$filename = basename($_FILES['uploaded_file']['name']);

$allowed = array('pdf', 'mp3', 'm3u', 'wav', 'bmp', 'jpeg', 'jpg', 'gif', 'png', 'mpg', 'mpeg', 'mov', 'avi', 'divx');

$ext = strtolower(pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION));

if(in_array($ext, $allowed)) && ($_FILES["uploaded_file"]["size"] < 262144000)) {

//Determine the path to which we want to save this file

$newname = dirname(__FILE__).'/upload/'.$filename;

//Check if the file with the same name is already exists on the server

if (!file_exists($newname)) {

//Attempt to move the uploaded file to it's new place

if ((move_uploaded_file($_FILES['uploaded_file']['tmp_name'],$newname))) {

echo "It's done! The file has been saved as: ".$newname;

} else {

echo "Error: A problem occurred during file upload!";

}

} else {

echo "Error: File ".$_FILES["uploaded_file"]["name"]." already exists";

}

} else {

echo "Error: Your file is not an accepted type, or is too large (250MB's or smaller). Please contact the site admin for help.";

}

} else {

echo "Error: No file uploaded";

}

?>

[/code]


Thanks for you help.

-Alex.
Copy linkTweet thisAlerts:
@SyCoDec 08.2008 — One too many )

[code=php]line: 16 if(in_array($ext, $allowed) && ($_FILES["uploaded_file"]["size"] < 262144000)) {
[/code]


It closed the if() so want expecting what it got.
Copy linkTweet thisAlerts:
@misteralexanderauthorDec 08.2008 — while that did take care of the parsing error, it seems as though (through my attempts) that ALL files are failing the check . . . a ".zip" file fails (as it should) but a ".jpg" file fails as well . . .

Urgh.

-Alex.
×

Success!

Help @misteralexander spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.20,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...