Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable image hotlinking #34

Closed
Fleshgrinder opened this issue Mar 8, 2012 · 10 comments
Closed

Disable image hotlinking #34

Fleshgrinder opened this issue Mar 8, 2012 · 10 comments

Comments

@Fleshgrinder
Copy link

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

@perusio
Copy link
Owner

perusio commented Mar 8, 2012

Hello Richard,

Nice idea. But I think that returning a [in-memory 1x1 transparent GIF](rfimusique.com rfimusique.local) 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.

@Fleshgrinder
Copy link
Author

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

@perusio
Copy link
Owner

perusio commented Mar 8, 2012

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

@Fleshgrinder
Copy link
Author

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.

@perusio
Copy link
Owner

perusio commented Mar 9, 2012

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.

@Fleshgrinder
Copy link
Author

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.

@perusio
Copy link
Owner

perusio commented Mar 9, 2012

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.

@Fleshgrinder
Copy link
Author

I will try it later today and give you feedback.

@perusio
Copy link
Owner

perusio commented Mar 11, 2012

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

@perusio perusio closed this as completed Mar 11, 2012
@perusio
Copy link
Owner

perusio commented Mar 11, 2012

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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants