Matomo-Tracking und Aufrufzaehlung absichern
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../app/analytics-relay.php';
|
||||
|
||||
$failures = 0;
|
||||
$assert = static function (string $label, bool $condition) use (&$failures): void {
|
||||
if ($condition) {
|
||||
echo "OK: {$label}\n";
|
||||
return;
|
||||
}
|
||||
$failures++;
|
||||
echo "FEHLER: {$label}\n";
|
||||
};
|
||||
|
||||
$environmentNames = [
|
||||
'APP_ENV',
|
||||
'APP_HOST',
|
||||
'APP_MARKETING_URL',
|
||||
'MATOMO_URL',
|
||||
'MATOMO_SITE_ID',
|
||||
'MATOMO_CONSENT_COOKIE_DOMAIN',
|
||||
'MATOMO_PRIVACY_CONFIRMED',
|
||||
];
|
||||
$previousEnvironment = [];
|
||||
foreach ($environmentNames as $name) {
|
||||
$previousEnvironment[$name] = getenv($name);
|
||||
}
|
||||
|
||||
putenv('APP_ENV=dev');
|
||||
putenv('APP_HOST=app.example.test');
|
||||
putenv('APP_MARKETING_URL=http://example.test:8765');
|
||||
putenv('MATOMO_URL=http://analytics.example.test/');
|
||||
putenv('MATOMO_SITE_ID=7');
|
||||
putenv('MATOMO_CONSENT_COOKIE_DOMAIN=.example.test');
|
||||
putenv('MATOMO_PRIVACY_CONFIRMED=1');
|
||||
|
||||
$visitorId = '0123456789abcdef';
|
||||
$cookies = [
|
||||
APP_ANALYTICS_CONSENT_COOKIE => APP_ANALYTICS_CONSENT_VERSION . ':granted',
|
||||
APP_ANALYTICS_VISITOR_COOKIE => $visitorId,
|
||||
];
|
||||
$server = [
|
||||
'HTTP_HOST' => 'example.test:8765',
|
||||
'HTTP_ORIGIN' => 'http://example.test:8765',
|
||||
'HTTP_SEC_FETCH_SITE' => 'same-origin',
|
||||
'SERVER_PORT' => '8765',
|
||||
'HTTP_USER_AGENT' => 'Relay-Testbrowser/1.0',
|
||||
'REMOTE_ADDR' => '203.0.113.42',
|
||||
];
|
||||
$payload = [
|
||||
'visitorId' => $visitorId,
|
||||
'pageUrl' => 'http://example.test:8765/passwort-zuruecksetzen.php?token=GEHEIM#formular',
|
||||
'pageTitle' => 'Kaffeeliste Passwort zurücksetzen',
|
||||
'referrerUrl' => 'http://app.example.test/login.php?source=GEHEIM',
|
||||
'resolution' => '1440x900',
|
||||
'language' => 'de-DE',
|
||||
'h' => 12,
|
||||
'm' => 34,
|
||||
's' => 56,
|
||||
];
|
||||
|
||||
$parameters = app_matomo_page_view_parameters($payload, $server, $cookies);
|
||||
$assert('Seiten-URL verliert Query und Fragment',
|
||||
($parameters['url'] ?? '') === 'http://example.test:8765/passwort-zuruecksetzen.php');
|
||||
$assert('Interner Referrer verliert Query und Fragment',
|
||||
($parameters['urlref'] ?? '') === 'http://app.example.test/login.php');
|
||||
$assert('Zufällige Besucherkennung wird unverändert genutzt', ($parameters['_id'] ?? '') === $visitorId);
|
||||
$assert('Browserdaten werden auf erlaubte Angaben begrenzt',
|
||||
($parameters['res'] ?? '') === '1440x900'
|
||||
&& ($parameters['lang'] ?? '') === 'de-DE'
|
||||
&& ($parameters['ua'] ?? '') === 'Relay-Testbrowser/1.0');
|
||||
$assert('Client-IP wird nicht an Matomo übergeben',
|
||||
!array_key_exists('cip', $parameters)
|
||||
&& !in_array('203.0.113.42', $parameters, true));
|
||||
|
||||
$capturedUrl = '';
|
||||
$capturedParameters = [];
|
||||
$forwardResult = app_matomo_forward_page_view(
|
||||
$parameters,
|
||||
static function (string $url, array $values) use (&$capturedUrl, &$capturedParameters): array {
|
||||
$capturedUrl = $url;
|
||||
$capturedParameters = $values;
|
||||
return ['ok' => true, 'status' => 204, 'error' => null];
|
||||
}
|
||||
);
|
||||
$assert('Relay verwendet den fest konfigurierten Matomo-Endpunkt',
|
||||
$forwardResult['ok']
|
||||
&& $capturedUrl === 'http://analytics.example.test/matomo.php'
|
||||
&& ($capturedParameters['idsite'] ?? '') === '7');
|
||||
|
||||
$mustReject = static function (array $changedPayload, array $changedServer, array $changedCookies): bool {
|
||||
try {
|
||||
app_matomo_page_view_parameters($changedPayload, $changedServer, $changedCookies);
|
||||
return false;
|
||||
} catch (Throwable) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
$deniedCookies = $cookies;
|
||||
$deniedCookies[APP_ANALYTICS_CONSENT_COOKIE] = APP_ANALYTICS_CONSENT_VERSION . ':denied';
|
||||
$assert('Ohne Einwilligung wird der Aufruf verworfen', $mustReject($payload, $server, $deniedCookies));
|
||||
|
||||
$foreignServer = $server;
|
||||
$foreignServer['HTTP_ORIGIN'] = 'https://evil.example';
|
||||
$foreignServer['HTTP_SEC_FETCH_SITE'] = 'cross-site';
|
||||
$assert('Fremder Ursprung wird verworfen', $mustReject($payload, $foreignServer, $cookies));
|
||||
|
||||
$foreignPage = $payload;
|
||||
$foreignPage['pageUrl'] = 'https://evil.example/gestohlen?token=1';
|
||||
$assert('Fremde Seiten-URL wird verworfen', $mustReject($foreignPage, $server, $cookies));
|
||||
|
||||
$wrongVisitor = $payload;
|
||||
$wrongVisitor['visitorId'] = 'fedcba9876543210';
|
||||
$assert('Nicht zum Cookie passende Besucherkennung wird verworfen', $mustReject($wrongVisitor, $server, $cookies));
|
||||
|
||||
$relaySource = file_get_contents(APP_ROOT . '/app/analytics-relay.php') ?: '';
|
||||
$assert('Relay-Code liest die Client-IP nicht', !str_contains($relaySource, "['REMOTE_ADDR']"));
|
||||
|
||||
foreach ($previousEnvironment as $name => $value) {
|
||||
$value === false ? putenv($name) : putenv($name . '=' . $value);
|
||||
}
|
||||
|
||||
if ($failures > 0) {
|
||||
echo "\n{$failures} Relay-Prüfung(en) fehlgeschlagen.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "\nFirst-Party-Matomo-Relay: alle Prüfungen bestanden.\n";
|
||||
Reference in New Issue
Block a user