Short Upload Script - Can you see any security issues?

Discussion in 'PHP' started by explorer, May 21, 2009.

  1. #1
    This code allows people to upload images to a site. It comes from here.

    The script works perfectly. Can anyone see any security issues with it?

    <?php
    //define a maxim size for the uploaded images in Kb
     define ("MAX_SIZE","100"); 
    
    //This function reads the extension of the file. It is used to determine if the file  is an image by checking the extension.
     function getExtension($str) {
             $i = strrpos($str,".");
             if (!$i) { return ""; }
             $l = strlen($str) - $i;
             $ext = substr($str,$i+1,$l);
             return $ext;
     }
    
    //This variable is used as a flag. The value is initialized with 0 (meaning no error  found)  
    //and it will be changed to 1 if an errro occures.  
    //If the error occures the file will not be uploaded.
     $errors=0;
    //checks if the form has been submitted
     if(isset($_POST['Submit'])) 
     {
     	//reads the name of the file the user submitted for uploading
     	$image=$_FILES['image']['name'];
     	//if it is not empty
     	if ($image) 
     	{
     	//get the original name of the file from the clients machine
     		$filename = stripslashes($_FILES['image']['name']);
     	//get the extension of the file in a lower case format
      		$extension = getExtension($filename);
     		$extension = strtolower($extension);
     	//if it is not a known extension, we will suppose it is an error and will not  upload the file,  
    	//otherwise we will do more tests
     if (($extension != "jpg") && ($extension != "jpeg") && ($extension != "png") && ($extension != "gif")) 
     		{
    		//print error message
     			echo '<h1>Unknown extension!</h1>';
     			$errors=1;
     		}
     		else
     		{
    //get the size of the image in bytes
     //$_FILES['image']['tmp_name'] is the temporary filename of the file
     //in which the uploaded file was stored on the server
     $size=filesize($_FILES['image']['tmp_name']);
    
    //compare the size with the maxim size we defined and print error if bigger
    if ($size > MAX_SIZE*1024)
    {
    	echo '<h1>You have exceeded the size limit!</h1>';
    	$errors=1;
    }
    
    //we will give an unique name, for example the time in unix time format
    $image_name=time().'.'.$extension;
    //the new name will be containing the full path where will be stored (images folder)
    $newname="images/".$image_name;
    //we verify if the image has been uploaded, and print error instead
    $copied = copy($_FILES['image']['tmp_name'], $newname);
    if (!$copied) 
    {
    	echo '<h1>Copy unsuccessfull!</h1>';
    	$errors=1;
    }}}}
    
    //If no errors registred, print the success message
     if(isset($_POST['Submit']) && !$errors) 
     {
     	echo "<h1>File Uploaded Successfully! Try again!</h1>";
     }
    
     ?>
    
     <!--next comes the form, you must set the enctype to "multipart/frm-data" and use an input type "file" -->
     <form name="newad" method="post" enctype="multipart/form-data"  action="">
     <table>
     	<tr><td><input type="file" name="image"></td></tr>
     	<tr><td><input name="Submit" type="submit" value="Upload image"></td></tr>
     </table>	
     </form>
    Code (php):
     
    explorer, May 21, 2009 IP
  2. NatalicWolf

    NatalicWolf Peon

    Messages:
    262
    Likes Received:
    14
    Best Answers:
    0
    Trophy Points:
    0
    #2
    I added a fix. Check it out...First line: $allowedexts and the in_array where you checked extensions.

    
    <?php
    $AllowedExts=array('jpg','png','jpeg','.gif');
    //define a maxim size for the uploaded images in Kb
     define ("MAX_SIZE","100"); 
    
    //This function reads the extension of the file. It is used to determine if the file  is an image by checking the extension.
     function getExtension($str) {
             $i = strrpos($str,".");
             if (!$i) { return ""; }
             $l = strlen($str) - $i;
             $ext = substr($str,$i+1,$l);
             return $ext;
     }
    
    //This variable is used as a flag. The value is initialized with 0 (meaning no error  found)  
    //and it will be changed to 1 if an errro occures.  
    //If the error occures the file will not be uploaded.
     $errors=0;
    //checks if the form has been submitted
     if(isset($_POST['Submit'])) 
     {
        //reads the name of the file the user submitted for uploading
        $image=$_FILES['image']['name'];
        //if it is not empty
        if ($image) 
        {
        //get the original name of the file from the clients machine
           $filename = stripslashes($_FILES['image']['name']);
        //get the extension of the file in a lower case format
          $extension = getExtension($filename);
           $extension = strtolower($extension);
        //if it is not a known extension, we will suppose it is an error and will not  upload the file,  
        //otherwise we will do more tests
    // Added by Thomas - thomas.p.andrews[at]gmail.com
     if (!in_array($extension,$AllowedExts))  
           {
            //print error message
              echo '<h1>Unknown extension!</h1>';
              $errors=1;
           }
           else
           {
    //get the size of the image in bytes
     //$_FILES['image']['tmp_name'] is the temporary filename of the file
     //in which the uploaded file was stored on the server
     $size=filesize($_FILES['image']['tmp_name']);
    
    //compare the size with the maxim size we defined and print error if bigger
    if ($size > MAX_SIZE*1024)
    {
        echo '<h1>You have exceeded the size limit!</h1>';
        $errors=1;
    }
    
    //we will give an unique name, for example the time in unix time format
    $image_name=time().'.'.$extension;
    //the new name will be containing the full path where will be stored (images folder)
    $newname="images/".$image_name;
    //we verify if the image has been uploaded, and print error instead
    $copied = copy($_FILES['image']['tmp_name'], $newname);
    if (!$copied) 
    {
        echo '<h1>Copy unsuccessfull!</h1>';
        $errors=1;
    }}}}
    
    //If no errors registred, print the success message
     if(isset($_POST['Submit']) && !$errors) 
     {
        echo "<h1>File Uploaded Successfully! Try again!</h1>";
     }
    
     ?>
    
     <!--next comes the form, you must set the enctype to "multipart/frm-data" and use an input type "file" -->
     <form name="newad" method="post" enctype="multipart/form-data"  action="">
     <table>
        <tr><td><input type="file" name="image"></td></tr>
        <tr><td><input name="Submit" type="submit" value="Upload image"></td></tr>
     </table>   
     </form>
    
    
    PHP:
     
    NatalicWolf, May 21, 2009 IP
    explorer likes this.
  3. explorer

    explorer Well-Known Member

    Messages:
    463
    Likes Received:
    40
    Best Answers:
    0
    Trophy Points:
    110
    #3
    Thanks for that NatalicWolf. +Rep.

    Do you (or does anyone else) think there's any prospect of someone uploading something damaging using this script?
     
    explorer, May 22, 2009 IP
  4. olddocks

    olddocks Notable Member

    Messages:
    3,275
    Likes Received:
    165
    Best Answers:
    0
    Trophy Points:
    215
    #4
    olddocks, May 22, 2009 IP
  5. explorer

    explorer Well-Known Member

    Messages:
    463
    Likes Received:
    40
    Best Answers:
    0
    Trophy Points:
    110
    #5
    Thanks olddocks,

    If I set the upload folder 777, what problems could really result?

    A hacker could read the contents - that's not a problem. He could write jpg, png, jpeg, and gif files to the folder. But, since none of these are executable, he's then stuck and no damage could be done.

    Am I missing something obvious?
     
    explorer, May 23, 2009 IP
  6. Barti1987

    Barti1987 Well-Known Member

    Messages:
    2,703
    Likes Received:
    115
    Best Answers:
    0
    Trophy Points:
    185
    #6
    So many problems. They can write a PHP script to read the contents of the other files, hack into the server, etc..

    You never need to chmod folders 777, just don't allow write for the public.

    Peace,
     
    Barti1987, May 23, 2009 IP
  7. explorer

    explorer Well-Known Member

    Messages:
    463
    Likes Received:
    40
    Best Answers:
    0
    Trophy Points:
    110
    #7
    Thanks for that azizy.

    Yet, I've read elsewhere (Post: Why chmod 777 is NOT a security risk) that the security risks assosiated with chmod 777 are exaggerated. Only if a hacker has already got into your server (I have a dedicated server) could chmod 777 cause a problem:

     
    explorer, May 23, 2009 IP
  8. marxfinder

    marxfinder Peon

    Messages:
    3
    Likes Received:
    0
    Best Answers:
    0
    Trophy Points:
    0
    #8
    I didnt test it but I think it could be possible to upload with fake extensions. Better to check both image-header and extension. Look at this uploader: cafewebmaster.com/smart-multi-uploader-and-thumbnail-creator-gif-jpeg-png-php (no urls allowed here)
     
    marxfinder, May 23, 2009 IP
  9. octalsystems

    octalsystems Well-Known Member

    Messages:
    352
    Likes Received:
    14
    Best Answers:
    0
    Trophy Points:
    135
    Digital Goods:
    1
    #9
    i do not think that the 777 will cause any problem if u r checking the file type before uploading that means u r not allowing any executable file to upload and i think in ur case u r only allowing the images to upload and any kind of image can not be execute . so u r ok with 777
     
    octalsystems, May 23, 2009 IP
  10. olddocks

    olddocks Notable Member

    Messages:
    3,275
    Likes Received:
    165
    Best Answers:
    0
    Trophy Points:
    215
    #10
    a malicious code can be embedded into a photo to exectute. Headers or extensions can be faked easily. The best measure would be dont allow any executions inside 777 folder.

    see this: http://mysql-apache-php.com/fileupload-security.htm

    Another way around is give ownership to apache to write on the folder. It would work with 775 instead of 777.
     
    olddocks, May 23, 2009 IP
    explorer likes this.
  11. octalsystems

    octalsystems Well-Known Member

    Messages:
    352
    Likes Received:
    14
    Best Answers:
    0
    Trophy Points:
    135
    Digital Goods:
    1
    #11
    plz tell me how one can execute this http://example.com/test.jpg or http://example.com/test.png or http://example.com/test.gif
    if on the server these file types are not set as executable?
     
    octalsystems, May 23, 2009 IP
  12. KDisk

    KDisk Peon

    Messages:
    58
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    0
    #12
    Without shell access, it is not easy
     
    KDisk, May 24, 2009 IP
  13. WoRLDLiFE

    WoRLDLiFE Peon

    Messages:
    116
    Likes Received:
    2
    Best Answers:
    0
    Trophy Points:
    0
    #13
    I would recommend to use basename() function to get file name . :D:)
     
    WoRLDLiFE, May 25, 2009 IP
  14. explorer

    explorer Well-Known Member

    Messages:
    463
    Likes Received:
    40
    Best Answers:
    0
    Trophy Points:
    110
    #14
    Thanks for your thoughts everybody. Looking at the link olddocks provided, I followed Method 2:

    chown -R apache uploadsfolder
    chmod -R 770 uploadsfolder

    Also, following a little of Method 1, in the uploadsfolder .htaccess I placed as additional security to prevent other files appearing in the folder:

    <Files ^(*.jpeg|*.jpg|*.png|*.gif)>
    order deny,allow
    deny from all
    </Files>


    And, to prevent anyone seeing the contents of uploadsfolder and further prevention of script executions:

    Options -Indexes
    Options -ExecCGI
    AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi


    Doing all this has made access to that folder a lot more difficult. In fact, I need to use puTTY now myself to get into it and see what's there!

    Again, I'd be grateful to hear everyone's thoughts on these security measures.
     
    explorer, May 26, 2009 IP
  15. K1llswitch

    K1llswitch Member

    Messages:
    84
    Likes Received:
    1
    Best Answers:
    0
    Trophy Points:
    28
    #15
    Yes, I would check file type for allowing/disallowing because extensions can be faked.
     
    K1llswitch, May 26, 2009 IP
  16. olddocks

    olddocks Notable Member

    Messages:
    3,275
    Likes Received:
    165
    Best Answers:
    0
    Trophy Points:
    215
    #16
    True! Just use getimagesize() function. If it fails dont allow to upload. You can get the width, height, picture type as well with this function.

     
    olddocks, May 27, 2009 IP