[Catalyst-dev] Re: Catalyst::Engine::Apache X-Forwarded-* Handling

John Shields johnmshields at gmail.com
Thu May 24 01:03:10 GMT 2007


On 5/23/07, Andy Grundman <andy at hybridized.org> wrote:
> >
> > I've attached a patch to Catalyst/Engine/Apache.pm (1.11) to handle
> > the Apache "X-Forwarded-*" headers correctly (as I understand them).
> > The patch does two things:
> >
> > 1. Changed the current IP address determination to use the *first* IP
> > in the "X-Forwarded-For" header rather than the last. My understanding
> > of the header is that the first IP address will be the first client IP
> > and the last will be the closest proxy server IP. My understanding of
> > the $c->req->address field is that this should indicate the original
> > client IP, not the closest proxy server.
>
> I think we need to stick with the current X-Forwarded-For
> implementation because you can't trust any proxy server outside of
> your own network.  So using the last IP address means you get the IP
> of what your own proxy server saw, not the actual IP of the client,
> which may even be a useless private IP address.
>
> See http://www.openinfo.co.uk/apache/index.html for example, they
> discuss this more.
>

I think the existence of that Apache module (mod_extract_forwarded)
clarifies that the "X-Forwarded-For" header is next to useless unless
you do some munging or just trust it.

If the engine always picks off the last IP then you are assuming that
there is only one proxy server between you (Perl/Catalyst) and the
external network. This is not always true. For example, in our
environment we have a proxy server in a DMZ that proxies to our
internal "lite" server which then proxies to our mod_perl server. Thus
the last IP is just our DMZ proxy.

On the other hand, if you always pick off the first IP then you are
susceptible to external spoofing and possibly to some sort of attack.
(Although I think that depends upon what you DO with the IP...) In any
case it could be some internal IP of the client and not much more
useful than my DMZ proxy IP.

My position with this patch is that the IP returned by
$c->req->address should be the closest thing to the browser IP as
possible. Due to possible spoofing, there is no definitive way to
determine that the header is valid (as far as I can tell). So my
thinking is that Apache.pm should assume that the "X-Forwarded-For"
header is valid. The task of ensuring that the header is valid can be
delegated to your internal proxy server configuration. For example, if
I only want the closest IP to my DMZ proxy to be first, then I can use
the following directive in the proxy configuration to clear any
existing header:

RequestHeader unset X-Forwarded-For

I think that Apache.pm either needs to assume that the
"X-Forwarded-For" header is valid or have some more configuration
options to allow you to control the IP selection from the list. This
configuration could be similar to the "mod_extract_forwarded". But
again, I think this can (should) be handled at the proxy configuration
level rather than at the Catalyst code level.

> >
> > 2. Added the same processing for the "X-Forwarded-Host" header as this
> > can also have a comma separated list. In our environment we proxy
> > twice and the existing code creates a URI that has a host name of
> > "www.myhost.com, www.myhost.com". In this case as well, I think the
> > correct behavior is to pick up the first host name as this should be
> > the original requested host name.
>
> Shouldn't we also use the last forwarded host in this list too?
>
> -Andy
>

My thinking on this one is the same as for "X-Forwarded-For" except
that you *can* configure your proxies to forward the host name
(ProxyPreserveHost On) which I think results in all of the entries
added by your proxies being the same. But there again, theoretically
the first entry in the list is supposed to represent the host name
used by the client to access the site. Thus, if I want to create a new
URL for the user I should use that first host name (spoofing
notwithstanding). Also, again you can have your external facing proxy
server clean up the header if you are worried about spoofing.

So my position for both of these headers is that by the time they
reach Catalyst they should be trusted and that the original meaning of
them should be honored. Otherwise, you end up needing to allow all
kinds of fine tuning via configuration in order to pick out the
correct IP or host name for your specific proxy configuration. I feel
like the decision of where to draw the line between external and
internal should be handled by those administering the proxy server
itself. This also allows for proxy server architecture changes without
modifying your Catalyst configuration.

Of course I could be all wrong! ;-) Thanks!

- John



More information about the Catalyst-dev mailing list