CodeIgniter Forums
Question about file uploads - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forumdisplay.php?fid=20)
+--- Forum: Archived Development & Programming (https://forum.codeigniter.com/forumdisplay.php?fid=23)
+--- Thread: Question about file uploads (/showthread.php?tid=10037)



Question about file uploads - El Forum - 07-16-2008

[eluser]thurting[/eluser]
Hello,

I am working on a project that requires users to upload an image file via a form and have some questions. I would like to store these images securely so users cannot view them. I'm thinking about saving them above the web root, but I would also like to be able to view them through a web based admin area I am setting up. Is there any trickery (Apache or PHP based) that would allow me to have the files stored on the web root of my site, but only be available to users with admin credentials (maybe set up a script that does auth, then spits out the files with proper headers - e.g. web root at /htdocs and http://mysite.com/images/view/blah.jpg will do auth and read a file at /private/images/blah.jpg)? If not, I'm thinking the best course of action is simply to store the images with random names so it would be difficult for a user without access to the db or fs to access them.

Thanks.


Question about file uploads - El Forum - 07-17-2008

[eluser]xpix[/eluser]
make the folder name random


Question about file uploads - El Forum - 07-17-2008

[eluser]Rick Jolly[/eluser]
Yea, I'd store the images above the web root. Then have the controller stream the image. You could do that directly in the controller, or have any view call the controller from an image tag.
Code:
<img src="image_controller/download/file_name">
Here is an untested download helper function modified from another framework. It won't force download like CI's helper, but show the image in the browser window.
Code:
public function download($filepath)
{
if (is_file($filepath))
{
  // Get the real path
  $filepath = str_replace('\\', '/', realpath($filepath));

  // Get extension
  $extension = pathinfo($filepath, PATHINFO_EXTENSION);

  // Set filesize
  $filesize = filesize($filepath);

  // Load the mime types
  @include(APPPATH.'config/mimes'.EXT);
            
  // Set a default mime if we can't find it
  if ( ! isset($mimes[$extension]))
  {
   $mime = 'application/octet-stream';
  }
  else
  {
   $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
  }

  // Generate the server headers
  header('Content-Type: ' . $mime);
  header('Content-length: ' . $filesize);
  readfile($filepath);
}
}



Question about file uploads - El Forum - 07-17-2008

[eluser]thurting[/eluser]
Thanks for the replies. xpix, this is obviously the easiest solution, but I am worried about security. Rick, in terms of high security, that's what I was thinking. Thanks for the example. I had forgotten about that handy mime lookup table. In this case, I think I'm going to go with the more secure option. Anyone else have another solution? Thanks again.


Question about file uploads - El Forum - 07-17-2008

[eluser]thurting[/eluser]
Hi Rick,

I haven't played with your code, but I came back to ctrl+c as a starting point, and noticed what looks to be a pretty severe vulnerability. Because $filepath is taken from the URL, it can be manipulated by the user; e.g. ../../../etc/passwd. Now if ../../../etc/passwd existed on your fs, the user would be able to access that file given your implementation. I know you said this was untested, but if you have it deployed, you should patch it immediately.


Question about file uploads - El Forum - 07-18-2008

[eluser]Rick Jolly[/eluser]
That's worth mentioning. The example was a starting point and we all know to validate any inputs. I didn't include any controller code, but in the controller you'd validate that the file name is actually a file and not a path. Only then you'd append the file name to the path of your choice and call the download function.

Of course, you wouldn't actually be able to pass a "/" in the img tag without it being treated as another parameter.