cactus / go-camo

A secure image proxy server
MIT License
254 stars 48 forks source link

Feature request: Limit destination to port 80 and 443 #30

Closed niklasf closed 5 years ago

niklasf commented 5 years ago

Hi, is there a way to configure go-camo so that it will only proxy requests to the standard http(s) ports?

That seems like a useful defense in addition to the DNS rebinding mitigations.

dropwhile commented 5 years ago

There currently is no such support.

I think this kind of filtering might be better done on the url generation side of things. For example https://github.com/cactus/go-camo/blob/master/examples/python-base64.py could be rewritten as:

#!/usr/bin/env python3

import hashlib
import hmac
import base64
from urllib.parse import urlsplit

CAMO_HOST = 'https://img.example.com'

def camo_url(hmac_key, image_url):
    url = urlsplit(image_url)

    if url.scheme not in ('http', 'https'):
        # depending on application code, it may be more appropriate
        # to return a fixed url placeholder image of some kind (eg. 404 image url),
        # an empty string, or raise an exception that calling code handles.
        return ""

    if url.scheme == 'https':
        # pass through https, no need to proxy it to get security lock
        return image_url

    if url.scheme == 'http' and ':' in url.netloc and not url.netloc.endswith(':80'):
        # depending on application code, it may be more appropriate
        # to return a fixed url placeholder image of some kind (eg. 404 image url),
        # an empty string, or raise an exception that calling code handles.
        return ""

    hmac_key = hmac_key.encode() if isinstance(hmac_key, str) else hmac_key
    image_url = image_url.encode() if isinstance(image_url, str) else image_url

    b64digest = base64.urlsafe_b64encode(
        hmac.new(hmac_key, image_url, hashlib.sha1).digest()
    ).strip(b'=')
    b64url = base64.urlsafe_b64encode(image_url).strip(b'=')
    requrl = '%s/%s/%s' % (CAMO_HOST, b64digest, b64url)
    return requrl

print(camo_url("test", "http://golang.org/doc/gopher/frontpage.png"))
# 'https://img.example.org/D23vHLFHsOhPOcvdxeoQyAJTpvM/aHR0cDovL2dvbGFuZy5vcmcvZG9jL2dvcGhlci9mcm9udHBhZ2UucG5n'
print(camo_url("test", "http://golang.org:8080/doc/gopher/frontpage.png"))
#

Another option would be an outbound firewall rule (make sure it is REJECT so camo immediately fails the request instead of having to wait for timeout) blocking any outbound tcp that isn't port 80, 8080, or 443.

Proxy filtering should probably be reserved for cases that can't be easily determined a-priori, such as dns rebind prevention. The current AllowList support in the proxy is probably a bit of a misfeature, but it is retained for backwards compatibility.

niklasf commented 5 years ago

Makes sense. Thanks for the quick reply.

dropwhile commented 5 years ago

I added the code sample above to the examples directory here