Skip to content

Security: URL-encode cip to prevent tracking-parameter injection #151

Description

@ilyyeees

Security issue

MatomoTracker::getRequest() appends the value of cip directly to the outbound tracking query string without URL-encoding it:

'&cip=' . $this->ip

Most neighboring tracking parameters are passed through urlencode(), but cip is concatenated raw.

If an application passes an untrusted or insufficiently validated value to setIp(), query-string delimiters such as & can be interpreted as additional tracking parameters rather than remaining part of the IP value.

For example, a value shaped like:

1.2.3.4&additional_parameter=value

is serialized as multiple query parameters instead of one cip value.

This becomes security-relevant in server-side tracking integrations that also configure token_auth, because the injected parameters are forwarded in the same authenticated tracking request. Fields that Matomo normally restricts to authenticated tracking requests may therefore be unintentionally submitted using the application’s configured token.

Affected code

The PHP tracker currently stores the value supplied to setIp() and later appends it without encoding.

Conceptually, the vulnerable behavior is:

'&cip=' . $this->ip

It should instead use the same encoding applied to other tracking parameters:

'&cip=' . urlencode($this->ip)

The sibling .NET tracker appears to contain the same raw-concatenation pattern and may need the equivalent fix.

The Java tracker already encodes cip, so encoding the value would also make behavior consistent across the official tracker implementations.

Security impact

The library itself does not control where an integrating application obtains the IP value. Applications may derive it from reverse-proxy or CDN headers.

Although integrations should validate such headers before passing them to setIp(), raw query-string concatenation makes the SDK unsafe when validation is absent, incomplete, or later regresses.

Possible consequences include injection of additional authenticated tracking fields, such as:

  • an alternate site ID;
  • historical timestamps;
  • manual geolocation fields;
  • other tracking parameters accepted when token_auth is present.

The configured token is not disclosed, but the tracker may act as a confused deputy by attaching it to parameters that were not intentionally configured by the application.

Recommended fix

  1. URL-encode the cip value at the serialization sink:
'&cip=' . urlencode($this->ip)
  1. Consider validating setIp() input with an IP-address parser as defense in depth.

  2. Add a regression test that supplies a value containing & and confirms that:

    • only one cip parameter is produced;
    • no additional query parameters are created;
    • the original value remains encoded inside cip.
  3. Apply the equivalent fix to piwik-dotnet-tracker if the same behavior remains there.

Encoding at the sink is important even when callers are expected to validate input, because query construction should not allow a parameter value to change the structure of the generated request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions