Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Disable image hotlinking #34

Closed
Fleshgrinder opened this Issue · 10 comments

2 participants

Richard Fussenegger António P. P. Almeida
Richard Fussenegger

Hi perusio!

Just implemented this in my own website and thought you might be interested to include this as an optional (or standard) part of your nginx configuration. It works with Imagecache and AJAX field UI of Drupal 7. I only tested it with a Drupal 7 installation, Drupal 6 might need some adoptions.

blacklist-hotlinking.conf

### This file implements a blacklist for certain referrers. It's used to
### prevent hotlinking of your images. Must be included in server context.


## Initialize our nested if variable. For more information on this
## technique have a look at: http://wiki.nginx.org/RewriteMultiCondExample
set $invalid "";

## If the file exists set our variable to 1.
if (-f $request_filename) {
    set $invalid 1;
}

## Add here all valid referrers, use the RegEx pattern
## ~(mydomain.com) do include all possible subdomains and the
## pattern ~(mydomain.) to include all possible subdomains and
## TLDs as well. You can separate many hosts with a pipe.
## ~(mydomain.com|google.|bing.|yahoo.)
##
## More info at:
## http://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html
## http://linuxsysadminblog.com/2009/08/using-wildcards-in-nginx-valid_referers/
valid_referers none blocked server_names ~(example.com|google.|bing.|yahoo.);

## Check the actual referer and if it's invalid set our variable to
## the previously set variable + 1.
if ($invalid_referer) {
    set $invalid "${invalid}1";
}

## If the file exists and we have an invalid referer somebody is
## hotlinking our image - prevent it!
if ($invalid = 11) {
    #return 444;
    return 403;
    ## It's wise to redirect to an image hosting service, so your
    ## bandwith won't be used to serve this picture.
    #rewrite ^.*\.(gif|jpe?g|png)$ http://example-imagehoster.com/hotlinking.jpg last;
}

static-files.conf

### This template can be included in location contexts where static
### files are going to be served.

access_log off;
expires max;
## No need to bleed constant updates. Send the all shebang in one fell
## swoop.
tcp_nodelay off;
## Set the OS file cache.
open_file_cache max=3000 inactive=120s;
open_file_cache_valid 45s;
open_file_cache_min_uses 2;
open_file_cache_errors off;

Implementation example in sites-available/drupal.conf

    ## We allow hotlinking of our banners!
    location ~* /files/banner/ {
        ## Include configuration for static files.
        include static-files.conf;
    }

    ## Drupal 7 generated image handling, i.e., imagecache in core. See:
    ## https://drupal.org/node/371374.
    location ~* /files/styles/ {
        ## Prevent hotlinking!
        include blacklist-hotlinking.conf;

        ## Include configuration for static files.
        include static-files.conf;

        ## Delegate to Drupal if file doesn't exist for Imagecache
        ## generation of the image.
        try_files $uri @drupal;
    }

    ## All static files will be served directly.
    location ~* ^.+\.(?:css|js|jpe?g|gif|ico|png|html|xml)$ {
        ## Prevent hotlinking!
        include blacklist-hotlinking.conf;

        ## Include configuration for static files.
        include static-files.conf;
    }

By default I allow no referers (valid users might not submit one), blocked (if users try to to stay anonymous) our server names, our domain including all subdomains and of course the big search engines including all subdomains and TLDs.

Hope you like it and keep up the great work!

Regards
Richard

António P. P. Almeida
Owner

Hello Richard,

Nice idea. But I think that returning a in-memory 1x1 transparent GIF is "nicer". It weights 43 bytes and there's no potential for mucking up the CSS on a page that tries to include the images.

valid_referers none blocked server_names *.example.com example.* www.example.info/galleries/ ~\.google\.;

if ($invalid_referer) {
   return 302 /empty;
} 

location /empty {
    internal;
    empty_gif;
}

I'll update the config soon with this option. Also offering the 403 and 444 possibilities.

Thanks.

Richard Fussenegger

Of course that's also a great idea. Please bare in mind that your current code will break Imagecache (took me a lot of time to find a solution that works).

Include the redirect as well, it's kind of a tradition to display a funny picture if somebody hotlinks your images (most of the time your images get hotlinked in forums, and with a funny picture you can at least make some advertisement for your website and if people aren't interested they have something to laugh about).

Regards
Richard

António P. P. Almeida
Owner

This needs to be better thinked out. I haven't noticed your -f $request_filename. I think it can be done differently. The referer module should run at the access phase after the rewrite phase but well before the try files phase.

Needs further work. It's just that using -f is not very Nginxy. That's to be used only and only if you really want to act based on the existence of a file that is created outside of the request context, like for example, a maintenance page is detected on the filesystem, then act accordingly.

António

Richard Fussenegger

I know that, but I couldn’t find a way to achieve Imagecache support without using a third party module (like ngnx_lua) or using -f $request_filename. If you can think of any other possibility I’d love to know about it. But I guess until nginx 2 there won’t be any.

Richard

PS: That’s also the reason why I suggested it as optional.

António P. P. Almeida
Owner

Perhaps we can do away with the referer module and instead use the map directive for hostnames. This way there's no access phase mixed in the process.TBD.

Richard Fussenegger

I had such a configuration, but the problem was that I couldn’t block hotlinking of Imagecache generated images. I have to check if the file exists in order to decide if I have to forward the request to Drupal (respectively Imagecache) or not. If we could find out how to include Imagecache as valid referer it would work with a map. But every configuration I tried blocked the AJAX requests and Imagecache directly stopped generating images.

António P. P. Almeida
Owner

That shouldn't happen. The try files phase happens much later than the rewrite phase. Here's a config sketch done in the morning train. Caveat emptor.

At the http level:

map $http_referer $not_allowed_referer {
    hostnames;
    default 1;
    *.google.* 0;
    *.example.com 0;
}

At the image serving location level:

#...
if ($not_allowed_referer) {
   return 302 /empty;
}
#...

The /empty location:

location /empty {
    internal;
    empty_gif;
}

Needs testing. YMMV.

Richard Fussenegger

I will try it later today and give you feedback.

António P. P. Almeida
Owner

I have added hotlinking protection. Now it returns a mere message that states: No image hotlinking allowed. with a 200 status code.

António P. P. Almeida perusio closed this
António P. P. Almeida
Owner

Just to add that the referer check happens at the rewrite phase. If the host is allowed then the $invalid_referer variable gets the value "" (empty string). If not allowed it's set to 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.