Skip to content
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

Closed
timdream opened this issue Feb 7, 2021 · 10 comments · Fixed by #20341
Closed

Matomo can be tricked to record spoofed X-Forwarded-For IPs #17202

timdream opened this issue Feb 7, 2021 · 10 comments · Fixed by #20341
Labels
c: Security For issues that make Matomo more secure. Please report issues through HackerOne and not in Github.
Milestone

Comments

@timdream
Copy link

timdream commented Feb 7, 2021

Problem

Considering the current implementation:

matomo/core/IP.php

Lines 99 to 125 in a31fd86

/**
* Returns the last IP address in a comma separated list, subject to an optional exclusion list.
*
* @param string $csv Comma separated list of elements.
* @param array $excludedIps Optional list of excluded IP addresses (or IP address ranges).
* @return string Last (non-excluded) IP address in the list or an empty string if all given IPs are excluded.
*/
public static function getFirstIpFromList($csv, $excludedIps = null)
{
$p = strrpos($csv, ',');
if ($p !== false) {
$elements = explode(',', $csv);
foreach ($elements as $ipString) {
$element = trim(Common::sanitizeInputValue($ipString));
if(empty($element)) {
continue;
}
$ip = \Matomo\Network\IP::fromStringIP(IPUtils::sanitizeIp($element));
if (empty($excludedIps) || (!in_array($element, $excludedIps) && !$ip->isInRanges($excludedIps))) {
return $element;
}
}
return '';
}
return trim(Common::sanitizeInputValue($csv));
}

With the following setup:

[client] -> [reverse proxy 1] -> [reverse proxy 2] -> [matomo Nginx]

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):

X-Forwarded-For: (client), (proxy 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:

curl -i -H"X-Forwarded-For: 8.8.8.8" "http://example.com/matomo/matomo.php?..."

When this happens, proxy 1 will be faithfully preserve the header passed in and append the client IP after it (2):

X-Forwarded-For: 8.8.8.8, (client), (proxy 1)

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

Thanks!

@diosmosis
Copy link
Member

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?

@timdream
Copy link
Author

timdream commented Feb 8, 2021

@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
Copy link
Member

tsteur commented Feb 8, 2021

Thanks @timdream would this be kind of a duplicate or related to #7060 ?

@timdream
Copy link
Author

timdream commented Feb 9, 2021

@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.

@tsteur tsteur added the c: Security For issues that make Matomo more secure. Please report issues through HackerOne and not in Github. label Feb 9, 2021
@tsteur
Copy link
Member

tsteur commented Feb 10, 2021

There can be a solution 1.1 where Mamoto will rely on proxy 1 (the first public-facing proxy) to remove the spoofed header. However this is not always possible (not possible for Apache AFAIK)

that was my first thought would be the preferred solution as AFAIK in #10404 it says

The RFC at https://tools.ietf.org/html/rfc7239 makes it clear that we should extract the first IP from the header HTTP_X_FORWARDED_FOR

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.

@tsteur
Copy link
Member

tsteur commented Jun 29, 2021

BTW if you have a WAF then you may be able to configure to block any request having a X_FORWARDED_FOR request set.

@tsteur
Copy link
Member

tsteur commented Jul 15, 2021

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.

@MrIsak
Copy link

MrIsak commented Feb 25, 2022

BTW if you have a WAF then you may be able to configure to block any request having a X_FORWARDED_FOR request set.

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.
But this is not up to the application to deal with IMO.

If the XFF header is present, and the "proxy_client_headers[] = HTTP_X_FORWARDED_FOR" is set,
the application must be able to trust the chain of IP's. And use the first as client.

@tsteur
Copy link
Member

tsteur commented May 10, 2022

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 ([General] proxy_ip_read_last_in_list=1).

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.

@jane-twizel
Copy link

Will need to be communicated in the developer changelog as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: Security For issues that make Matomo more secure. Please report issues through HackerOne and not in Github.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants