0

I have the following in my config as a reverse proxy for images:

location ~ ^/image/(.+) {
    proxy_pass http://example.com/$1;
}

The problem is that not all images will be example.com images and so we need to pass in the full url. If I try:

location ~ ^/image/(.+) {
    proxy_pass $1;
}

I get an error:

invalid URL prefix in "https:/somethingelse.com/someimage.png"

2 Answers2

0

The question is quite vague, but, based on the error message, what you're trying to do is perform a proxy_pass entirely based on the user input, by using the complete URL specified after the /image/ prefix of the URI.

Basically, this is a very bad idea, as you're opening yourself to become an open proxy. However, the reason it doesn't work as in the conf you supplied is due to URL normalisation, which, in your case, compacts http://example into http:/example (double slash becomes single), which is different in the context of proxy_pass.

If you don't care about security, you can just change merge_slashes from the default of on to off:

merge_slashes off;
location …

Another possibility is to somewhat related to nginx proxy_pass and URL decoding

location ~ ^/image/.+ {
    rewrite ^ $request_uri;
    rewrite ^/image/(.*) $1 break;
    return 400;
    proxy_pass $uri; # will result in an open-proxy, don't try at home
}

The proper solution would be to implement a whitelist, possibly with the help of map or even prefix-based location directives:

location ~ ^/image/(http):/(upload.example.org)/(.*) {
    proxy_pass $1://$2/$3;
}

Do note that, as per the explanation in the begginning, the location above is subject to the merge_slash setting, so, it'll never have the double // by default, hence the need to add the double // manually at the proxy_pass stage.

cnst
  • 25,870
  • 6
  • 90
  • 122
  • That's a good point re:open proxy. I plan on making sure it is an image (eg ends in .png, .jpg, etc). Also implement the secure link module as described here https://www.endpoint.com/blog/2016/05/25/caching-resizing-reverse-proxying-image-server-nginx. Will those methods be enough to make it ok to use in production? –  Nov 24 '17 at 02:56
  • @kristen The `Content-Type` response header tells you the MIME type of the content being served; not sure how DDG does it, I presume they have a bit more logic than just nginx.conf. :-) TBH, if I were to block one filetype from an open proxy, images would probably be it, so, whitelisting the images hardly adds any protection. Using [`secure_link`](http://nginx.org/r/secure_link) might address security concerns, but only if the whole logic is implemented correctly (e.g., to only generate the links based on input from trusted users). – cnst Nov 24 '17 at 22:25
0

I would use a map in this case

map $request_uri  $proxied_url {
   # if you don't care about domain and file extension
   ~*/image/(https?)://?(.*)   $1://$2;

   # if you want to limit file extension
   ~*/image/(https?)://?(.*\.(png|jpg|jpeg|ico))$   $1://$2;
   # if you want to limit file extension and domain

   ~*/image/(https?)://?(abc\.xyz\.com/)(.*\.(png|jpg|jpeg|ico))$   $1://$2$3;
   default "/404";
}

Then in your proxy pass part you would use something like below

location /image/ {
   proxy_pass $proxied_url;
}

I have given three different example depending how you want to handle it

Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265