views:

62

answers:

4

How do you prevent hotlinking on a standalone Java webapp server like Tomcat?

+2  A: 

I'm not sure whether it already exists, but you could easily write a Filter that checks whether there's a Referer header that matches the appropriate pattern (as described in the link you've posted).

EDIT: What the article you've linked to describes is a rule based on the Referer HTTP header (which is sent by browsers to indicate from which page the link was obtained). The following rules in .htaccess on Apache Httpd with mod_rewrite more or less mean, if the Referer header doesn't match the http://(www\\.)?yoursite\\.com pattern, then redirect to /images/hotlink.jpeg.

RewriteEngine on 
RewriteCond %{HTTP_REFERER} . 
RewriteCond %{HTTP_REFERER} !^http://(www\\.)?yoursite\\.com [NC] 
RewriteRule \\.(gif|jpe?g)$ /images/hotlink.$1 [L]

Filters are a standard mechanism in webapps for intercepting requests before they're sent to the servlet for processing (and they can chose not to redirect to the servlet if needed).

You would override the doFilter(ServletRequest request, ServletResponse response, FilterChain chain) in your Filter, test whether request.getHeader("Referer") matches the right pattern, if so, call chain.doFilter(request, response), otherwise send a redirection response to some other image (that would say "hotlink" or whatever), possibly with a 403 status code.

Bruno
@Bruno: I don't really understand this, could you tell me more? :) You mean that all the requests made, say, to pictures, from the page I served myself already include a 'Referer' I could check for? Or would I need, in addition to the Filter, to also add a specific 'Referer'?
Webinator
HTTP specifies a 'Referrer' header, that contains the location from which the client obtained the address he is currently requesting. For embedded images it's the URL of the page that the browser is currently loading.
Tassos Bassoukos
@Tassos Bassoukos indeed, but the HTTP spec spells it `Referer` (single "r") unfortunately.
Bruno
+2  A: 

You can check for an appropriate referer as Bruno said.

Every HTTP request contains a referer header that contains the URL that linked to the current URL being requested (or, for images, the page that referenced the image). In your case, it should contain an appropriate referer URL, which should belong to your own site.

In order to detect disallowed referers, I think you could use a filter like http://www.tuckey.org/urlrewrite/ . You can configure a simple rule that matches every image request not coming from your own site, and forbid the access or rewrite that URL to a custom 'Hotlinking not allowed' image.

jjmontes
In fact, the urlrewrite filter I mentioned is Java equivalent for Apache's mod_rewrite, which is used in the article you referenced, so it should be pretty easy for you to mimic the behaviour described in that article.
jjmontes
+1  A: 

Here's an example filter implementation:

public class HotLinkFilter implements Filter{

    private final Map<Pattern, Pattern> PATTERNS =
        new ConcurrentHashMap<Pattern, Pattern>();

    private void addPatterns(final String targetPattern,
        final String referrerPattern){
        PATTERNS.put(Pattern.compile(targetPattern),
            Pattern.compile(referrerPattern));
    }

    @Override
    public void init(final FilterConfig config) throws ServletException{
        @SuppressWarnings("unchecked")
        final Enumeration<String> parameterNames =
            config.getInitParameterNames();
        while(parameterNames.hasMoreElements()){
            final String nextParam = parameterNames.nextElement();
            if(nextParam.startsWith("pattern")){
                final String[] patterns =
                    config.getInitParameter(nextParam).split("\\s+");
                if(patterns.length == 2){
                    addPatterns(patterns[0], patterns[1]);
                }
            }
        }
    }

    @Override
    public void doFilter(final ServletRequest request,
        final ServletResponse response,
        final FilterChain chain) throws IOException, ServletException{

        if(request instanceof HttpServletRequest){
            final HttpServletRequest hsr = (HttpServletRequest) request;
            final String referrer = hsr.getHeader("Referer");
            boolean valid = true;
            if(referrer != null){
                final String requestUrl = hsr.getRequestURL().toString();
                for(final Entry<Pattern, Pattern> entry : PATTERNS.entrySet()){
                    if(entry.getKey().matcher(requestUrl).matches()
                        && !entry.getValue().matcher(referrer).matches()){
                        valid = false;
                        break;
                    }
                }
            }
            if(valid){
                chain.doFilter(request, response);
            } else{
                // this is probably not the correct thing to do
                throw new ServletException("Hotlinking not allowed");
            }

        }

    }

    @Override
    public void destroy(){
    }

}

It uses a map of Regex patterns. If a request matches the pattern on the left side and a referrer is present, then we check if the referrer matches the pattern on the right side. You can configure this in the web.xml:

<filter>
    <filter-name>Hotlink-Filter</filter-name>
    <filter-class>com.yourcompany.HotLinkFilter</filter-class>
    <init-param>
        <param-name>pattern1</param-name>
        <param-value>http://.*\.mysite.com/.*\.(jpe?g|gif|png) 
        http://.*\.mysite.com/.*&lt;/param-value&gt;
    </init-param>
</filter>
seanizer
Are you sure it's `hsr.getHeader("HTTP_REFERER")` and not `hsr.getHeader("Referer")`? I think it ought to be the actual header name, not some configuration key-word.
Bruno
My bad. It's "Referer". Thanks.
seanizer
+1  A: 

Use Tuckey's URLRewriteFilter (as mentioned by others already indirectly). From the documentation:

<rule>
    <name>Blocked Inline-Images</name>
    <note>
        Assume we have under http://www.quux-corp.de/~quux/ some pages with inlined GIF graphics. These graphics are
        nice, so others directly incorporate them via hyperlinks to their pages. We don't like this practice because
        it adds useless traffic to our server.

        While we cannot 100% protect the images from inclusion, we can at least restrict the cases where the browser
        sends a HTTP Referer header.

        RewriteCond %{HTTP_REFERER} !^$
        RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC]
        RewriteRule .*\.gif$ - [F]
    </note>
    <condition name="referer" operator="notequal">^$</condition>
    <condition name="referer" operator="notequal">^http://www.quux-corp.de/~quux/.*$&lt;/condition&gt;
    <from>.*\.gif$</from>
    <set type="status">403</set>
    <to>null</to>
</rule>

<rule>
    <name>Blocked Inline-Images example 2</name>
    <note>
        RewriteCond %{HTTP_REFERER} !^$
        RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\.html$
        RewriteRule ^inlined-in-foo\.gif$ - [F]
    </note>
    <condition name="referer" operator="notequal">^$</condition>
    <condition name="referer" operator="notequal">.*/foo-with-gif\.html$</condition>
    <from>^inlined-in-foo\.gif$</from>
    <set type="status">403</set>
    <to>null</to>
</rule>
kaliatech