New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Matomo can be tricked to record spoofed X-Forwarded-For IPs #17202
Comments
Hi @timdream, thanks for creating this issue. Is this behavior in Matomo causing a problem for you specifically? Do you know of a client that does this and is causing a problem? |
@diosmosis It did not cause a problem for me! I am just trying to document what I've found that may be problematic in terms of spoofing and attack vector. I did not spot such attack/spoofing taking place on my own instance. |
@tsteur Yes the issue is related (see note), but I wouldn't say it is a duplicate because the research I made here contains new information on the current implementation. |
that was my first thought would be the preferred solution as AFAIK in #10404 it says
So changing it to last IP could potentially break things again etc. Another workaround might be to use other headers depending on what can be configured that can only be set by the public facing proxy? I don't know if that's the same problem for Apache. Mostly seeing this so far as a documentation issue to mention it in the docs for setting up the proxy if it's not mentioned yet. |
BTW if you have a WAF then you may be able to configure to block any request having a |
We are adding a new config setting for this in #17765 Maybe in Matomo 5 we make it default to read the last entry. However, depending on the setup the last IP might be an internal IP and it could break things. To be evaluated with Matomo 5 whether we'll change the default. |
You are definitely on to something. AFAIK is it more or less best practice to replace the XFF(X-Forwarded-For) header on all traffic passing the first proxy that handles incoming traffic, with the real client source IP. Since you can never trust a client. If the XFF header is present, and the "proxy_client_headers[] = HTTP_X_FORWARDED_FOR" is set, |
We have prepared this feature in https://github.com/matomo-org/matomo/pull/17765/files Ideally we enable this security feature by default to have the IP detection more secure ( Then we update the guide in https://matomo.org/faq/how-to-install/faq_98/ to mention people may need to disable this feature if the first IP should be used. |
Will need to be communicated in the developer changelog as well. |
Problem
Considering the current implementation:
matomo/core/IP.php
Lines 99 to 125 in a31fd86
With the following setup:
Since each proxy appends the IP of the incoming connection according to RFC7239,
X-Forwarded-For
will be this by the time the connection reaches Nginx (1):Matomo currently extracts the first IP from the list, since #10404. This is correct until you realized the "client" can pretended to be a proxy as well, by including its own
X-Forwarded-For
header, like this:When this happens, proxy 1 will be faithfully preserve the header passed in and append the client IP after it (2):
And Matomo will extract
8.8.8.8
as the client IP.Solutions
Solution 1: Trust the "transparent proxies"
If Matomo decided the first "client" IP can always be trusted, we can still extract the first IP address, and this issue can be closed as "works as intended." But it will in risk of having the analytics data being polluted. Brutal force login detection can be tricked too.
Solution 2: Extract the last IP address
This would involve reverting #10404 and go back to extract the last IP address. Since the original
proxy_ips[]
configuration is still intact, when facing a header like (2), above, a correctly configured Matomo will be able to exclude the "proxy 1" IP address.Notes
HTTP_CF_CONNECTING_IP
,HTTP_CLIENT_IP
, etc) behaves and if Matomo have to do things differently between these and theX-Forwarded-For
header.proxy_ips[]
pretty well in the documentation. We may consider doubling down on the documentation effort when we pick either solution for this issue.IP::getFirstIpFromList
but it didn't change the word "last" in the function comment right above it :)Thanks!
The text was updated successfully, but these errors were encountered: