Supporting the Forwarded header (RFC 7239)

Maxim Dounin mdounin at
Mon Aug 7 22:04:38 UTC 2017


On Sun, Aug 06, 2017 at 09:11:03PM +0300, Vasiliy Faronov wrote:

> Hi nginx developers,
> As you know, RFC 7239 defines a Forwarded header to replace the zoo of
> X-Forwarded-* with a single extensible syntax.
> There seems to be growing interest in deploying Forwarded. For
> example, aiohttp, a popular Python library, recently started reading
> Forwarded by default.
> I'd like to see a $proxy_add_forwarded variable in nginx -- similar in
> essence to $proxy_add_x_forwarded_for -- and I'd like to try and
> contribute a patch.
> Can you tell me if this sounds like a good idea to you, and if yes,
> which of the following approaches you prefer? (I'm sorry for the wall
> of text)

We've previously considered adding Forwarded support, though 
postponded it as it seems to be somewhat different from 
X-Forwarded-For / X-Real-IP we do support, and we haven't seen any 
practial implementations.

> 1. $proxy_add_forwarded blindly appends a "for=..." after a comma,
> just like $proxy_add_x_forwarded_for does. (This cannot be emulated in
> config because of ticket #1316; also, nginx's $remote_addr is not
> quite in the format required by RFC 7239.)
> The problem here: suppose an external user sends:
>     Forwarded: for=injected;by="
> If you just append to this, you get:
>     Forwarded: for=injected;by=", for=real
> This puts the burden on the receiving application to parse this
> robustly and recover "for=real". They can't just split on comma
> (because comma can occur inside a valid quoted-string), they need more
> logic. It's doable, they just have to be aware of the problem and
> actively mitigate it.

Certainly blindly adding a value without checking the header 
syntax looks like a bad idea.  It opens an obvious vulnerability 
as RFC-complain parsing by the upstream server will produce an 
incorrect "for=injected" result.

It is also not clear how to deal with such syntactically incorrect 
headers: removing the previous headers will obviously result in an 
information loss, while "fixing" them also looks wrong.

>From this point of view, X-Forwarded-For is much better, as it 
does not require to parse anything.

> 2. Parse any incoming Forwarded headers into an internal
> representation, then re-serialize it with an added element.
> This is obviously more expensive. But, if you think that supporting
> Forwarded is a good idea, then eventually you want to support it in
> ngx_http_realip_module and wherever else nginx looks at X-Forwarded-*.
> Then you need to parse anyway.

This looks like an overkill to me, especially considering that 
Forwarded can have arbitrary extensions.  

> 3. Validate the syntax of any incoming Forwarded headers with a regex.
> If they are valid, append to them. If they are invalid, replace them
> with a single "for=unknown" (explicitly allowed by RFC 7239), and
> append to that.

I don't see how this is explicitly allowed by RFC 7239.
And anyway this is an information loss, see above.

> 4. Do any of the above, but in a third-party module, where one could
> experiment more freely.
> I think requiring a third-party module to support Forwarded will just
> lead to everybody sticking with X-Forwarded-*, or else trying to
> emulate it in config with poor results. It's just not enough of a
> feature on its own.

It should be more or less trivial to implement config-based 
emulation of $proxy_add_forwarded, using map{} and appropriate 
regular expression checks.  The main problem here is ticket #1316, 
yet it probably should be addressed separately.

On the other hand, such approach also faces the problem on 
what to do with syntactically invalid Forwarded headers, and I 
don't think I know a solution.

(Also, the interesting part is probably using Forwarded in the 
realip module, and this is certainly can't be done as a 3rd party 
module without re-implementing the whole module.)

> Then again, maybe RFC 7239 was a bad idea and everybody *should* just
> stick to X-Forwarded-*.

I think _eventually_ there should be some standard on this, and 
RFC 7239 looks like a step in the right direction.  It seems to 
have various problems though, and it may be a good idea to 
postpone the implementation till it is clear how to address these 
problems and/or an updated RFC to address them is available.

Maxim Dounin

More information about the nginx-devel mailing list