Jump to content
horst

Protect original Images in site/assets/files/

Recommended Posts

Hi, on a site I want to disable access to original images and only allow to access thumbnails and watermarked image variations.

EDIT:
A good solution for protecting original images can be found a bit down in this thread:

 

Old content of this initial post:

Spoiler

To create the watermarked images I use the PiM, what creates filenames with prefix "pim_". For individual thumbnails I use the Thumbnails-Module and define the prefix like "thumb_".

A simple way would be to use a htaccess file in top of the assets folder with something like this for example:

The only downside is that I cannot access the original images from the admin backend. While it isn't an option to use a proxy-script from the front-end, it could be an option for the admin.

Any ideas how I could access the originals when in admin backend?


# deny access to all jpegs and pngs
<FilesMatch "^.*\.(jpg|png)$">
    Deny from all
</FilesMatch>

# allow access to pim_ - variations (my watermarked images)
<FilesMatch "^pim_.*\.(jpg|png)$">
    Allow from all
</FilesMatch>

# allow access to thumb_ - prefixed files (my individual thumbnails)
<FilesMatch "^thumb_.*\.(jpg|png)$">
    Allow from all
</FilesMatch>

# allow access to 100px thumbnails as used by default in PW-Admin when images-field is configured to display thumbnails
<FilesMatch "^.*\.0x100\.(jpg|png)$">
    Allow from all
</FilesMatch>
<FilesMatch "^.*\.100x0\.(jpg|png)$">
    Allow from all
</FilesMatch>

 

  • Like 3

Share this post


Link to post
Share on other sites

Hi Horst, 

maybe you could set up a rule which takes care of the referrer, something like this?

RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?yourdomain.com [NC]

Only if the referrer equals your domain, allow access to the originals. Problem could be that your server does not use http referrers...

Share this post


Link to post
Share on other sites

 Only if the referrer equals your domain, allow access to the originals. Problem could be that your server does not use http referrers...

Maybe something like domain/processwire, but a referrer header could be set to anything one want, I guess.

Better could be to use the REMOTE_ADDR. Does someone know how a variable for apache could be set by script? If this could be done everytime a superuser log in I could set this IP to a apache / environment variable for which access is allowed.

Share this post


Link to post
Share on other sites

Private file handling is your solution. You would abstract all files from public access by doing something like... 

/files.php?file=myfile.jpg

vs.

/myfile.jpg

Then in your private file handler you can check permissions and show the users the correct image according to their access level. I'm not sure if idea is native to PW.

  • Like 1

Share this post


Link to post
Share on other sites

@sshaw: what you are describing is what I have called proxy-script in my opening post. I don't want that for frontend output, because it is not needed and I want avoid overhead. All images that should have public access can be accessed directly. Others (the originals) are blocked to the public. Thats fine.

The only downside is that an admin in backend also cannot access the originals.

What I'm looking for is how I can define this in htaccess file:

all images that are now blocked for direct access should be not served but handled by a proxy script.

Share this post


Link to post
Share on other sites
To do it your way, maybe use RedirectMatch in place of your first FilesMatch
RedirectMatch \.(png|gif|jpg|jpeg)$ http://example.com/image_handler.php
I think mod rewrite will be your ideal solution, several examples below that will get you close. Personally I'd rewrite all image requests to my proxy script, then store my allowed prefixes in an array and simply loop through the array. It's not the best solution, but it's simple and allows for some scalability. 
 
in .htaccess 
 
Redirect all images to proxy script
RewriteEngine On
RewriteBase /
RewriteRule ^(.*)\.(jpg|png|jpeg|gif)$ watermark.php?image=$1.$2 [NC,L]
in your proxy script watermark.php
$prefixes[] = "thumb_";
$prefixes[] = "100x0";

foreach ($prefixes[] as $prefix) {
    if((substr(0,strlen($prefix)) == $prefix) xor ($user->isSuperuser())) {
        //return the image
    } else {
        //return a different image (maybe one that says access to this file is denied)
    }
}
Some rewrite examples that may help:
 
Redirect requests that don't originate from the host www.example.com (stop hotlinking) 
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !www.example.com [NC]
RewriteRule \.(gif|jpg|png)$ /images/go-away.png [R,NC]
Check for a login cookie and redirect if not logged in
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_COOKIE} !CookieName= [NC]
RewriteRule .* http://www.example.com/members/login.php [L]

Share this post


Link to post
Share on other sites

coming back to this: I have made good progress!
 
I do not use the htaccess file in site/assets/files/ anymore but have edited the htaccess file in pw root folder. Somewhere at top of the mod_rewrite directives I have added my lines that should redirect requests to original images to a proxy-script and let others pass through:
 

htaccess with Pim1 and PW < 2.5.11

Spoiler

...

  # -----------------------------------------------------------------------------------------------
  # Optional: Set a rewrite base if rewrites aren't working properly on your server.
  # And if your site directory starts with a "~" you will most likely have to use this.
  # -----------------------------------------------------------------------------------------------

  RewriteBase /
  # RewriteBase /pw/
  # RewriteBase /~user/

    # -----------------------------------------------------------------------------------------------
    # CUSTOMSETTING : redirect original images to proxy-script - /pwimg.php?fn=...
    # -----------------------------------------------------------------------------------------------

    RewriteCond %{REQUEST_FILENAME} -f
    RewriteCond %{REQUEST_FILENAME} (^|/)site/assets/files/(.*?)/
    RewriteCond %{REQUEST_FILENAME} \.(jpg|jpeg|gif|png)$ [NC]
    RewriteCond %{REQUEST_FILENAME} !/pim_
   #RewriteCond %{REQUEST_FILENAME} !/(thumb_|thumb2_)
    RewriteCond %{REQUEST_FILENAME} !.*/.*?\.([0-9]+)x([0-9]+)\.(jpg|png|jpeg|gif)$ [NC]

    RewriteRule ^(.*)$ pwimg.php?fn=$1 [L,QSA]

  # -----------------------------------------------------------------------------------------------
  # Access Restrictions: Keep web users out of dirs that begin with a period
  # -----------------------------------------------------------------------------------------------

...

 

 

 .htaccess with PW 2.5.11+ / PW 3+

        # -----------------------------------------------------------------------------------------------
        # CUSTOMSETTING : redirect original images to proxy-script - /pwimg.php?fn=...
        # -----------------------------------------------------------------------------------------------

        RewriteCond %{REQUEST_FILENAME} -f
        RewriteCond %{REQUEST_FILENAME} (^|/)site/assets/files/(.*?)/

        RewriteCond %{REQUEST_FILENAME} \.(jpg|jpeg|gif|png)$ [NC]
        RewriteCond %{REQUEST_FILENAME} !-piacrop
        RewriteCond %{REQUEST_FILENAME} !-piacontain
        RewriteCond %{REQUEST_FILENAME} !-pim2-full
        RewriteCond %{REQUEST_FILENAME} !-blogthumb
        RewriteCond %{REQUEST_FILENAME} !.*/.*?\.([0-9]+)x([0-9]+)\.(jpg|png|jpeg|gif)$ [NC]
        RewriteRule ^(.*)$ pwimg.php?fn=$1 [L]

Now from all existing images the originals get redirected to the proxy-script and the others will delivered directly by apache. Requests to none existing imagefiles get answered by a 404.
 
So as everything seems to work fine, the RewriteConditions could be optimized a bit.
 
 
---- pwimg.php ----

<?php

// check filename
$imgFilename = isset($_GET['fn']) ? preg_replace('/[^a-zA-Z0-9_\-\/\.@]/', '', $_GET['fn']) : false;
$imgFilename = is_file(dirname(__FILE__) . "/$imgFilename") && is_readable(dirname(__FILE__) . "/$imgFilename") ? dirname(__FILE__) . "/$imgFilename" : false;
if (false == $imgFilename) {
    header('HTTP/1.1 404 Not Found');
    exit(2);
}

// check imagetype
$imgType = getImageType($imgFilename);
if (false == $imgType) {
    header('HTTP/1.1 403 Forbidden');
    header('Content-type: image/jpeg');
    exit(1);
}

// bootstrap PW
require_once(dirname(__FILE__) . '/index.php');

// check user-account
if (! wire('user')->hasRole('superuser|editor')) {
    header('HTTP/1.1 403 Forbidden');
    header('Content-type: ' . $imgType);
    exit(1);
}

// collect infos
$maxAge = (60 * 60 * 2); // 2 hours
$imgTimestamp = filemtime($imgFilename);
$imgExpiration = intval(time() + $maxAge);

// create headers
$imgHeaders = array();
$imgHeaders[] = 'Content-type: ' . $imgType;
$imgHeaders[] = 'Content-Length: ' . filesize($imgFilename);
$imgHeaders[] = 'Date: ' . gmdate('D, d M Y H:i:s',time()) . ' GMT';
$imgHeaders[] = 'Last-Modified: ' . gmdate('D, d M Y H:i:s',$imgTimestamp) . ' GMT';
$imgHeaders[] = 'Expires: ' . gmdate('D, d M Y H:i:s', $imgExpiration) . ' GMT';
$imgHeaders[] = 'pragma: cache';
$imgHeaders[] = "Cache-Control: no-transform, private, s-maxage={$maxAge}, max-age={$maxAge}";

// send headers
foreach($imgHeaders as $imgHeader) header($imgHeader);

// send file
$errorCode = @readfile($imgFilename) === FALSE ? 1 : 0;

// and exit
exit($errorCode);



// --- functions ---

function getImageType($fn, $returnAsInteger = false) {

    $types1 = array(1 => 'gif', 2 => 'jpg', 3 => 'png');
    $types2 = array('gif' => 1, 'jpg' => 2, 'jpeg' => 2, 'png' => 3);

    if (function_exists('exif_imagetype') && isset($types1[@exif_imagetype($fn)])) {
        $success = $types1[exif_imagetype($fn)];
    }
    if (!isset($success) && function_exists('getimagesize')) {
        $info = @getimagesize($fn);
        if (isset($info[2]) && isset($types1[$info[2]])) {
            $success = $types1[$info[2]];
        }
    }
    if (!isset($success)) {
        $extension = strtolower(pathinfo($fn, PATHINFO_EXTENSION));
        if (isset($types2[$extension])) {
            $success = $types1[$types2[$extension]];
        }
    }

    if (!isset($success)) return false;

    return true === $returnAsInteger ? $types2[$success] : $success;
}
  • Like 5

Share this post


Link to post
Share on other sites

Hi @horst,

Im looking for an exact same solution, website is showing images, but can not be accessed through going directly to the URL.

I tried your solution, but also the page where the image is displayed is getting a 404 response for this image.

I also have de pwimg.php as a template and page /pwimg/ instead of a 'normal' php file, is that maybe a problem?

I hope you can help me with this!
Thnx.

Share this post


Link to post
Share on other sites

Please read & follow my solution exactly. The way you described doesn't work & it isn't meant to do so.
It describes a way to only protect the original images. Therefor you need to deny all access to them via htaccess file. In a second step, you enable access for superusers (or who ever you like) via a proxy (php) script. Nothing (!) is embedded into your PW-installation, only the htaccess file.

Share this post


Link to post
Share on other sites

Thanks for your reply. Indeed there was some misunderstanding. I got your solution working now and should be fine for now. Thanks @horst :).

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By totoff
      Dear all,
      tomorrow I'll have to introduce some editors on how to upload and edit images in image fields in Processwire. The site in question is quite image rich and I've spent some time to serve the appropriate images via scrcset already cropped for different screen sizes.
      However, the backend offers a lot of possibilities to manipulate images and most of them I'm not familiar with because as a developer I've never spent much thinking on what all the features might be good for … Yet I'm concerned that my editors work on images in the backend may interfere with my optimization strategies in the templates.
      Is there a general rule of thumb what content editors should do or not do with images in order not to break srcset strategies etc.? Maybe there is already a source of information you could point me to?
      Your recommendations are much appreciated!
    • By totoff
      Dear all,
      I'm upgrading an older side with the new custom fields for images feature as of 3.0.142. My image field is set to "Automatic" and holds a bunch of images together with their respective description on each page. New custom fields include "caption" among others and to make my live easier I I'm trying to populate "caption" with the value from the (default) description field. But unfortunately I can't seem to find out how to save the newly set values. This is my code:
      <?php foreach (page()->images as $image) { $image->set('caption', $image->description); bd($image->caption); echo files()->render("markup/views/view-card-image-fancybox.php", array('image'=>$image)); } ?> <?php $page->save(); bd($page->save()); ?> This sets the value as intended (see screenshot) but doesn't save it permanently to the database. What am I doing wrong?
      Thanks!
       

    • By dalibor
      I'm using hook for retrieving data from image metadata (IPTC) after image upload and filling them to some fields. When I choose server-side image resize, everything works well. But when I use client-side resize (which si much faster and client comfortable  - it uses PWImageResizer.js) image metadata are lost during resizing - I have downloaded the file after resize and checked for EXIF, IPTC etc. and they are not present.  Using PW 3.0.165. Tried to look into PWImageResizer.js but uhh it's too javascreepy to me:) 
    • By RDC
      Is the right way to get Processwire to skip a folder (ie not process it as its running a different CMS) to put
      RewriteCond %{REQUEST_URI} !^/shop/.*$
      just before 
      RewriteRule ^(.*)$ index.php?it=$1 [L,QSA]
      in .htaccess?
    • By fruid
      I'm using custom fields for images (as mentioned here: processwire.com/blog/posts/pw-3.0.142) for a specific images-field, the images of which need to have an HTML-caption. It works fine so far. Now I'm trying to import pages that use this field. I first exported some pages of that kind to see what the spreadsheet would look like. It seems to not separate the captions for each images but instead put all images in one cell and all captions in another.
      How could I go about importing these pages and assign the captions to the images accordingly?
×
×
  • Create New...