]> Sergey Matveev's repositories - public-inbox.git/commitdiff
wwwattach: prevent deep-linking via Referer match
authorEric Wong <e@80x24.org>
Mon, 23 Nov 2020 14:15:35 +0000 (14:15 +0000)
committerEric Wong <e@80x24.org>
Sat, 26 Dec 2020 19:42:16 +0000 (19:42 +0000)
This prevents `<img src=' tags from being used to deep-link
image attachments from HTML outside of the current host and
reduces potential for abuse.

Some browsers (e.g. Firefox) favor content detection and will
display images irrespective of the Content-Type header being
"application/octet-stream", and "Content-Disposition: attachment"
doesn't stop them, either.

Tested with dillo and Firefox.

Reported-by: Leah Neukirchen <leah@vuxu.org>
(cherry picked from commit 46cbc5a7a4ba917bd7154be3b6e6898420ff85d3)

lib/PublicInbox/WwwAttach.pm

index 0b2cda900ea00be6c181c288f3a5379f82588d87..09c66d0239cb40a5a3942f957154b4ec54fc1097 100644 (file)
@@ -9,6 +9,22 @@ use bytes (); # only for bytes::length
 use PublicInbox::EmlContentFoo qw(parse_content_type);
 use PublicInbox::Eml;
 
 use PublicInbox::EmlContentFoo qw(parse_content_type);
 use PublicInbox::Eml;
 
+sub referer_match ($) {
+       my ($ctx) = @_;
+       my $env = $ctx->{env};
+       my $referer = $env->{HTTP_REFERER} // '';
+       return 1 if $referer eq ''; # no referer is always OK for wget/curl
+
+       # prevent deep-linking from other domains on some browsers (Firefox)
+       # n.b.: $ctx->{-inbox}->base_url($env) with INBOX_URL won't work
+       # with dillo, we can only match "$url_scheme://$HTTP_HOST/" without
+       # path components
+       my $base_url = $env->{'psgi.url_scheme'} . '://' .
+                       ($env->{HTTP_HOST} //
+                        "$env->{SERVER_NAME}:$env->{SERVER_PORT}") . '/';
+       index($referer, $base_url) == 0;
+}
+
 sub get_attach_i { # ->each_part callback
        my ($part, $depth, $idx) = @{$_[0]};
        my $ctx = $_[1];
 sub get_attach_i { # ->each_part callback
        my ($part, $depth, $idx) = @{$_[0]};
        my $ctx = $_[1];
@@ -28,8 +44,14 @@ sub get_attach_i { # ->each_part callback
                                                                $ctx->{env});
                $part = $ctx->zflush($part->body);
        } else { # TODO: allow user to configure safe types
                                                                $ctx->{env});
                $part = $ctx->zflush($part->body);
        } else { # TODO: allow user to configure safe types
-               $res->[1]->[1] = 'application/octet-stream';
-               $part = $part->body;
+               if (referer_match($ctx)) {
+                       $res->[1]->[1] = 'application/octet-stream';
+                       $part = $part->body;
+               } else {
+                       $res->[0] = 403;
+                       $res->[1]->[1] = 'text/plain';
+                       $part = "Deep-linking prevented\n";
+               }
        }
        push @{$res->[1]}, 'Content-Length', bytes::length($part);
        $res->[2]->[0] = $part;
        }
        push @{$res->[1]}, 'Content-Length', bytes::length($part);
        $res->[2]->[0] = $part;