matomo icon indicating copy to clipboard operation
matomo copied to clipboard

`POST` to `matomo.php` Endpoint Returns 204 When Setting An `Origin` Header Not On The `cors_domains`

Open Zozman opened this issue 2 years ago • 5 comments

According to the documentation in the Matomo FAQ, CORS settings can be set by setting the cors_domains array in the config.ini.php to only allow certain domains to interact. However even if this is set, when sending a POST request to the matomo.php endpoint with a domain in the Origin request header that is not on the list still returns a 204 instead of an error.

Expected Behavior

When the Origin request header is set to a URL not on the list, the response should be some sort of 4xx response.

Current Behavior

Response is a 204

Possible Solution

Set behavior to return a 401 in this instance

Steps to Reproduce (for Bugs)

  1. Set a value in cors_domains in the config.ini.php to your domain.
  2. Verify these values on the System -> General Settings page under the Cross-Origin Resource Sharing (CORS) domains section.
  3. Send a POST request to the /matomo.php endpoint with the Origin header set to a value not in the cors_domains array (for example, https://evil.site
  4. Observe response is 204.

Context

This bug could allow malicious actors to hit endpoints from environments outside the allowed websites.

Your Environment

  • Matomo Version: 4.9.0
  • PHP Version: 8.0.17
  • Server Operating System: Linux (using Matomo docker container 4.0.9-apache)
  • Additionally installed plugins: None
  • Browser: Observed in Chrome 100.0.4896.127 and using Postman
  • Operating System: Mac OS X

Zozman avatar Apr 18 '22 23:04 Zozman

@Zozman thanks for the bug report, I think that's a bug I made in this PR, https://github.com/matomo-org/matomo/pull/19030.

Here should be instead of *, I believe should be a combination of cors_domains and sites domains in the database. ping @sgiehl

https://github.com/matomo-org/matomo/blob/dc753b2fc6e691d0830ec03143ae06c091474296/core/Tracker.php#L119

peterhashair avatar Apr 19 '22 02:04 peterhashair

should we assign this issue to the 5.0 milestone, since it's breaking changes?

peterhashair avatar Jun 06 '22 23:06 peterhashair

I'm not sure. Thinking of tracking I'm wondering if 204 is maybe actually the correct response since the tracking request was processed and we would want to avoid that because of https://github.com/matomo-org/matomo/blob/4.10.1/js/piwik.js#L2843-L2850 we would retry that tracking request.

A 204 would describe exactly above. Request was executed but no response/content.

AFAIK when sending a tracking request and there is a CORS error then the request itself was still executed. It's only that the response wasn't readable for the client. So we'd want to make sure to not send every tracking request twice.

tsteur avatar Jun 09 '22 10:06 tsteur

I guess that depends on the expectation. I would actually also assume that a configured cors domain would also disallow requests coming from other origins. In that case it would be correct to send a 403 Forbidden. We then would need to handle the 403 in another way in tracking js for sure.

sgiehl avatar Jun 09 '22 12:06 sgiehl

You can see this in the examples on https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS as well that these send HTTP 2XX (200 and 204) headers. And the browsers decided if the client can read the response.

Also

CORS failures result in errors but for security reasons, specifics about the error are not available to JavaScript. All the code knows is that an error occurred. The only way to determine what specifically went wrong is to look at the browser's console for details.

image

The browser console already shows correctly when a CORS error happens image

tsteur avatar Jun 09 '22 12:06 tsteur

@Zozman What exactly was you intention when creating the issue? Do you want to be able to block tracking requests that are not coming from the "correct" origin? In that case I guess this issue is actually the same as #20871 I'm asking because the CORS headers are actually only meant to disallow the browser from using content from other domains. But those headers itself wouldn't let any request fail. So the request would still be sent, but the response is discarded.

sgiehl avatar Jul 03 '23 09:07 sgiehl

@sgiehl Essentially our view is if an Origin not on the Cross-Origin Resource Sharing (CORS) domains list when one is set hits the endpoint, it should return a 401 or some other sort of error because that Origin shouldn't be allowed. It shouldn't return a "successful" response like a 204.

Zozman avatar Jul 03 '23 14:07 Zozman

@Zozman Ok, so it's around "blocking" requests. Personally I would avoid reusing the CORS config for this. As there could be good reasons for having another set of domains in the CORS config and in the list of domains you want to allow as tracking origin. Also how would you handle requests without an origin? Should those be discarded in any case then?

Note: In terms of security this won't have much benefit. If someone wants to send requests to you endpoint with random data, they can do it nevertheless by sending an origin header.

sgiehl avatar Jul 03 '23 15:07 sgiehl

@sgiehl I'm just confused about the fact that there's an explicit list of domains that should be allowed to send data to a Matomo instance and then treating requests from not those domains as OK. Without this, someone could setup malicious JavaScript on any website on the internet to hit a site's Matomo endpoint all day and get back successful responses. I understand anyone can hit it, but 20x means it's fine to do when it shouldn't be, right?

Zozman avatar Jul 03 '23 17:07 Zozman

I'm not very deep into the CORS specification. But either we should fully block a request if the origin doesn't match and return a 40x status. Or we allow tracking from any source and return a 204, like it currently is. Processing the request, but returning a 40x if the origin doesn't match might be even more confusing.

And yes, in theory any website could start tracking to a random instance. But the website configuration allows to discard all requests that don't match the configured domains. So you can at least prevent tracking random urls.

Anyway. I would suggest to introduce a new config that allows to enable blocking requests from origins that are not on the CORS list. That way anyone who wants to use that could enable it easily.

sgiehl avatar Jul 04 '23 09:07 sgiehl