1

I'm displaying a gallery of pictures which I store below the root for security. There are thumbnails for each jpeg. When displaying the gallery, I have successfully set

<img src='./php/getfile.php?file=xyz-thumb.jpg'></a>

getfile.php processes each thumbnail with the following code. When clicking on the thumbnail, the same code loads the larger version of the image.

I can already tell this code is slower than html and with potentially 20-30 thumbnails on a page, I am debating whether I need to keep the thumbnails visible to public_html for performance sake. Is there a quicker way to display the thumbnails? Is fpassthru() any quicker or more desirable for other reasons?

        // File Exists?
    if( file_exists($fullfilename)){

        // Parse Info / Get Extension
        $fsize = filesize($fullfilename);
        $path_parts = pathinfo($fullfilename);
        $ext = strtolower($path_parts["extension"]);

        // Determine Content Type
        switch ($ext) {
            case "pdf": $ctype="application/pdf"; break;
            case "exe": $ctype="application/octet-stream"; break;
            case "zip": $ctype="application/zip"; break;
            case "doc": $ctype="application/msword"; break;
            case "xls": $ctype="application/vnd.ms-excel"; break;
            case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
            case "gif": $ctype="image/gif"; break;
            case "png": $ctype="image/png"; break;
            case "jpeg":
            case "jpg": $ctype="image/jpg"; break;
            default: $ctype="application/force-download";
        }

        header("Pragma: public"); // required
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: private",false); // required for certain browsers
        header("Content-Type: $ctype");

        if ($mode == "view"){
            // View file
            header('Content-Disposition: inline; filename='.basename($fullfilename));
        } 
        else {
            // Download file
            header('Content-Disposition: attachment; filename='.basename($fullfilename));
        }

        header("Content-Transfer-Encoding: binary");
        header("Content-Length: ".$fsize);
        ob_clean();
        flush();
        readfile( $fullfilename );

    } else
        die('File Not Found:' . $fullfilename);
mseifert
  • 5,390
  • 9
  • 38
  • 100
  • 2
    What security does storing the thumbnails below the web root and serving it with php provide? – Bailey Parker Jan 08 '12 at 08:33
  • The security is for the full size images. The only reason to keep the thumbnails below root is the convenience of keeping them in the same directory. Otherwise I have to duplicate a complicated directory structure I have for the full sized images . I have hundreds of images grouped into many directories. – mseifert Jan 08 '12 at 08:37
  • Php coder, you can return images to users you want, it does not prevent copying just unautorized viewing, to improve this way viewing you can add caching – Aurimas Ličkus Jan 08 '12 at 08:39
  • I have presumed that the images would have to be viewed one at a time, right clicked on and saved. If they are fully visible, I had concern over someone being able to automate copying them. If this is being over paranoid or ineffective, I'm happy to hear it's not worth the effort or the performance impact. – mseifert Jan 08 '12 at 08:45
  • It sounds inefficient to me. If the full size images are stored outside of the web root and you only want users to access thumbnails to web users, why not create a thumbnails folder in your web route and pre-create thumbnails of all of the real images? You could write a small bash maintenance script to create the thumbnails or a run a small command each time you add an image to create a thumbnail in the web directory. – Bailey Parker Jan 08 '12 at 08:48
  • Thank you PhpMyCoder. Keeping thumbnails visible to the web makes sense -and you're right, it won't be hard to write a maintenance script to create the thumbnails. Is it worth it or common practice to keep the full-size images hidden below root? – mseifert Jan 08 '12 at 08:53
  • @mseifert If you don't want the web to be able to access the full size images, it makes sense. But otherwise, no. You should put them in the web root also and let your server serve the images instead of PHP (but still do the thumbnail pre-making). – Bailey Parker Jan 08 '12 at 08:57
  • 1
    Are normal, unauthorized users able to simply click on a link which will present them the full sized image at any point? If yes, then there's absolutely no point in serving them through PHP, since they're **publicly accessible**. It doesn't matter how much hand waving you do behind the scenes, if a publicly accessible URL points to the full sized image, that's all any client cares about. – deceze Jan 08 '12 at 08:59
  • I had originally set the link to point to a php file which loaded the full sized image. This masked the real url although the user could still right click and "save as" to download the file. At first I thought masking the url would help protect the wholesale grabbing of a whole directory or more. Based on feedback, there doesn't seem to be any benefit to masking the url - but if anyone thinks there is, I'd like to hear about it. – mseifert Jan 10 '12 at 04:08

2 Answers2

1

Based on your comments above, I would say this sounds like a very inefficient way to do it, mostly because it stops normal caching. If somebody is likely to automate scraping of your full size images, then they will find a way around it (e.g. Selenium RC).

If you're only concern is about someone scraping the images, then use another solution. Here are some other solutions:

The honeypot is a very common implementation.

Community
  • 1
  • 1
Dan Blows
  • 20,846
  • 10
  • 65
  • 96
  • Thanks for the links (and the honeypot suggestion) - it helps put it into perspective. I am new at web coding, so I am not even aware of all the ways of grabbing content. It sounds like screen scraping is the main method - and you're right, keeping the files hidden won't help. – mseifert Jan 08 '12 at 09:28
  • No problem. Selenium RC is one of the hardest to defend against, because it looks like a real browser, and you can slow it down so the traffic doesn't look suspicious. Realistically, if your content is attractive enough to be scraped, then it will happen - all you can do is deal with that reality. Watermarked images (can be done with Image Magick), throttling with Apache, honeypots, and tineye.com can all help. – Dan Blows Jan 08 '12 at 09:37
0

Anything involving PHP will have a noticeable performance hit, especially if you are checking the database to verify login credentials, etc.

You can improve your code by setting the correct cache headers, etc. But really the best solution is to let apache serve the images as static files. Apache is incredibly good at serving static files, you are never going to make a PHP script that works as well as Apache.

One way to provide reasonable security for static files is to put a very long and random string in the URL. So instead of:

./php/getfile.php?file=xyz-thumb.jpg

Use this as the URL:

./files/usBmN5CssIL47qRroC77n90juaQoREThBbFZUddGneEH5jOuX6JpU5cH6zH1Xa5-thumb.jpg

And make sure directory indexes are forbidden (so a user can't just visit ./files/.

With a random filename that long, even if a hacker were able to guess a billion URLs per second the universe would still have ended long before they guess every possible URL.

If you are worried about search engines/etc somehow indexing the file URLs after some other security breach, you could place all the files in a directory with another random name - and change the name of this directory regularly (perhaps daily, perhaps every 10 minutes). In this case you should leave a the old URL functional for a brief period of time after you rename it (perhaps with a symlink to the new directory name?).

At first glance this may sound less secure than checking a user's login credentials. But realistically, a random filename like that is much more secure than any username/password.

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • " _Anything involving PHP will have a noticeable performance hit._ ". This isn't true, especially the "noticeable" part. Of course there is a performance hit, but it doesn't have to be noticeable. – Nanne Jan 08 '12 at 09:10
  • +1 for the performance part, -1 for the random URL thing. If there's a link to the image anywhere on any page, i.e. if it is actually displayed anywhere, a random URL will hardly make any difference for anybody trying to scrape images. Yes, it's slightly more complex, but by no means any sort of "security". – deceze Jan 08 '12 at 09:14
  • Thank you for the info on making directory indexes forbidden. That is an important new piece of the puzzle for me. – mseifert Jan 08 '12 at 09:32
  • @Nanne we have tried many times, at the web programming company where I work, to get PHP to run as efficient as static apache files. It never worked succeeded, especially if anything involving a database is concerned. Static files always have much less latency, even with well configured ~$50,000 servers under off peak traffic. – Abhi Beckert Jan 08 '12 at 09:54
  • @deceze presumably anyone who has access to a page with an `` tag pointing to the images, will also have permission to see the actual images themselves. Having php verify the user's authentication credentials at this point is probably an order of magnitude faster than doing it once for every image on the page. – Abhi Beckert Jan 08 '12 at 09:56
  • There's a difference between that statement ("as efficient as") and the one in which you use the words "anything" and "noticable". Especially the "noticable" part. This is just something you can not state. – Nanne Jan 08 '12 at 10:03
  • @Nanne perhaps read my answer again, where I said "especially when checking login credentials/etc". No point passing it through PHP unless you're doing something like this. And when a page has 20 images, even a 5 millisecond delay becomes noticeable (a tenth of a second when you multiply them). Also, PHP blocks multiple simultaneous requests for any user with a specific session id, which will bring the browser down to loading images one at a time instead of 8 or more simultaneous requests. Since latency is usually longer than a small image download, one request at a time is very very bad. – Abhi Beckert Jan 08 '12 at 10:10
  • Likewise, perhaps read my comment again. Notice that I didn't say anything about the "checking login credentials/etc" part. Also notice that the word "especially" does not mean "only in the case of". Lastly, I don't agree with your "no point" at all, but the merits and/or problems of these kinds of scraping/hotlinking/whatever protections weren't really the point here. Suffices to say I said what I did and meant nothing more or less. " _Anything involving PHP will have a noticeable performance hit_ ". was the subject. You disagree. fine. This doesn't go anywhere, so I bid you a good day. – Nanne Jan 08 '12 at 10:16