Cookieless Tracking Fallbacks: Syntetyczne zszywanie sesji po stronie serwera

Spis treści
Przegląd architektury: Dylemat odrzucenia ciasteczek w analityce internetowej
We współczesnej analityce internetowej banery zgód (CMP) powodują powstawanie znaczących luk pomiarowych. Gdy użytkownicy odrzucają ciasteczka śledzące lub korzystają z przeglądarek wymuszających agresywne mechanizmy Intelligent Tracking Prevention (ITP), tradycyjne identyfikatory po stronie klienta, takie jak _ga, _gid czy identyfikatory odwiedzających w Matomo, są blokowane lub usuwane. W konsekwencji każde wyświetlenie strony przez użytkownika bez zgody jest rejestrowane jako pojedyncza, izolowana sesja, co czyni statystyczne analizy ścieżek konwersji, modele atrybucji oraz podróże użytkownika całkowicie nieprzydatnymi.
W celu przywrócenia ciągłości analitycznej bez naruszania przepisów RODO, dyrektywy ePrivacy oraz regulacji krajowych, możliwe jest wdrożenie syntetycznego zszywania sesji po stronie serwera (Server-Side Synthetic Session Stitching). W odróżnieniu od trwałego śledzenia cross-site lub fingerprintingu, syntetyczne zszywanie działa wyłącznie w warstwie serwera proxy lub przetwarzania brzegowego (np. Server-Side GTM, Cloudflare Workers lub własne punkty końcowe PHP/Node.js). Poprzez obliczanie ulotnego, rotowanego codziennie skrótu kryptograficznego z nieutrwalanych atrybutów sieciowych – takich jak skrócona podsieć IP, User-Agent oraz generowany na serwerze dzienny salt – sesje mogą być precyzyjnie odtwarzane dla celów statystycznych bez zapisywania jakichkolwiek danych na urządzeniu końcowym.

Instrukcja wdrożenia krok po kroku
Krok 1: Podstawy generowania ulotnych, zgodnych z prywatnością skrótów (Hash)
Utworzenie zgodnego z RODO syntetycznego identyfikatora sesji wymaga zastosowania trzech fundamentów architektonicznych:
- Skracanie podsieci IP: Pełny adres IP nigdy nie może być poddawany bezpośredniemu haszowaniu, gdyż nadal stanowi daną osobową. Adresy IPv4 muszą być maskowane do podsieci
/24(np.192.0.2.0), a adresy IPv6 do podsieci/48. - Dynamiczny dzienny Salt: Kryptograficzny, losowy ciąg (salt) musi być generowany po stronie serwera i automatycznie rotowany najpóźniej o północy (UTC). Gwarantuje to, że wszystkie skróty wygasają bezpowrotnie najpóźniej po 24 godzinach, co uniemożliwia wielodniowe profilowanie użytkowników.
- Jednokierunkowe szyfrowanie: Połączone atrybuty sieciowe muszą być przetwarzane za pomocą nieodwracalnego algorytmu haszującego, takiego jak SHA-256.
Krok 2: Projektowanie generatora skrótów w Edge Worker / Server-Side GTM
Poniższa implementacja produkcyjna w języku JavaScript pokazuje, w jaki sposób wygenerować zgodny z prywatnością syntetyczny identyfikator sesji jako zmienną niestandardową w Server-Side GTM lub w skrypcie Cloudflare Edge Worker bez wysyłania nagłówków HTTP Set-Cookie:
/**
* Generator syntetycznych skrótów sesji zgodny z prywatnością
* Cel: Server-Side GTM / Edge Workers
*/
const crypto = require('crypto');
function getSyntheticSessionId(requestHeaders, clientIp, dailySalt) {
// 1. Anonimizacja adresu IP (skrócenie IPv4 do /24 lub IPv6 do /48)
let maskedIp = '0.0.0.0';
if (clientIp.includes('.')) {
maskedIp = clientIp.split('.').slice(0, 3).join('.') + '.0';
} else if (clientIp.includes(':')) {
maskedIp = clientIp.split(':').slice(0, 3).join(':') + '::';
}
// 2. Ekstrakcja stabilnych nagłówków środowiska przeglądarki
const userAgent = requestHeaders['user-agent'] || 'unknown-ua';
const acceptLanguage = requestHeaders['accept-language'] || 'unknown-lang';
// 3. Budowa ładunku wejściowego z rotowanym codziennie saltem
const rawPayload = `${maskedIp}|${userAgent}|${acceptLanguage}|${dailySalt}`;
// 4. Obliczenie skrótu SHA-256
const syntheticHash = crypto
.createHash('sha256')
.update(rawPayload)
.digest('hex');
// Zwrócenie skróconego, 16-znakowego ulotnego ID
return 'syn_' + syntheticHash.substring(0, 16);
}
Krok 3: Integracja w kolektorze PHP WordPressa (functions.php)
W infrastrukturach oparłych na WordPressie v7.0.2, które wykorzystują własne punkty końcowe REST API do gromadzenia danych, obliczanie syntetycznej sesji da się zintegrować w pliku functions.php aktywnego motywu:
/**
* Narzędzie generowania syntetycznych skrótów sesji w PHP
* Cel: WordPress v7.0.2
*/
function lw_generate_synthetic_session_id() {
$client_ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
// Maskowanie IPv4 do /24
if ( strpos( $client_ip, '.' ) !== false ) {
$ip_parts = explode( '.', $client_ip );
$ip_parts[3] = '0';
$masked_ip = implode( '.', $ip_parts );
} else {
// Maskowanie IPv6 do /48
$ip_parts = explode( ':', $client_ip );
$masked_ip = implode( ':', array_slice( $ip_parts, 0, 3 ) ) . '::';
}
$user_agent = ( $_SERVER['HTTP_USER_AGENT'] ?? '' ) ?: 'unknown-ua';
$accept_language = ( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '' ) ?: 'unknown-lang';
// Pobranie lub utworzenie rotowanego codziennie salta z tabeli opcji
$daily_salt = get_transient( 'lw_daily_privacy_salt' );
if ( ! $daily_salt ) {
$daily_salt = wp_generate_password( 32, true, true );
// Wygaśnięcie salta o północy UTC (sekundy pozostałe do końca doby UTC)
set_transient( 'lw_daily_privacy_salt', $daily_salt, DAY_IN_SECONDS - ( time() % DAY_IN_SECONDS ) );
}
$raw_payload = $masked_ip . '|' . $user_agent . '|' . $accept_language . '|' . $daily_salt;
$sha256_hash = hash( 'sha256', $raw_payload );
return 'syn_' . substr( $sha256_hash, 0, 16 );
}
Krok 4: Mapowanie syntetycznych ID na platformy analityczne
Po wygenerowaniu, ulotny identyfikator sesji musi zostać przekazany do docelowej platformy analitycznej zamiast brakującego ciasteczka:
- Google Analytics 4 (GA4): Mapowanie wartości na parametr
client_idw Server-Side GTM przy jednoczesnym wymuszeniu parametrunon_personalized_ads=1oraz usunięciu nagłówków IP. - Matomo / Piwik PRO: Przypisanie skrótu do właściwości identyfikatora odwiedzającego po stronie serwera (
_idlubcid) i włączenie trybu bezciasteczkowego (Cookieless Mode). - Eksporty surowych danych do BigQuery: Zapisywanie wartości w osobnej kolumnie (np.
synthetic_session_id), co umożliwia modelowanie zapytań SQL oddzielających sesje ciasteczkowe od sesji zszywanych na serwerze.
Krok 5: Kontrola jakości i audyt zgodności
W celu weryfikacji poprawności i pełnej zgodności prawnej mechanizmu syntetycznego zszywania sesji, należy przeprowadzić trzystopniową procedurę testową:
- Weryfikacja nagłówków sieciowych: Kontrola odpowiedzi serwera w narzędziach deweloperskich przeglądarki (F12) potwierdzająca, że przy braku zgody nie są wysyłane żadne nagłówki HTTP
Set-Cookie. - Testy stabilności i kolizji: Symulacja zapytań z identycznych zakresów podsieci i potwierdzenie, że różne nagłówki User-Agent generują osobne skróty, natomiast kolejne wyświetlenia z tej samej przeglądarki zachowują ten sam identyfikator sesji.
- Audyt wygasania salta o północy: Wymuszenie wygaśnięcia dziennego salta i weryfikacja, czy wszystkie nowo generowane identyfikatory natychmiast ulegają zmianie, co dowodzi braku możliwości wielodniowego śledzenia.
Podsumowanie i mierzalna wartość dodana
Co da się osiągnąć dzięki instrukcji: Całkowite zastąpienie pofragmentowanych, pojedynczych odsłon w ruchu bez zgody na ciasteczka przez ulotny, serwerowy model rekonstrukcji sesji, działający bez zapisywania ciasteczek i bez przetwarzania pełnych adresów IP.
Wynikająca z tego wartość dodana:
- Przywrócenie analityki ścieżek konwersji i lejków: Statystyczne współczynniki konwersji, wskaźniki odrzuceń oraz wielokrokowe ścieżki nawigacji pozostają w przybliżeniu mierzalne także dla wizyt bez zgody na ciasteczka.
- Mniejsza ingerencja niż śledzenie ciasteczkami, ale bez zwolnienia z RODO: Na urządzeniu użytkownika nic nie jest zapisywane. Skrót z podsieci i nagłówków przeglądarki pozostaje jednak mimo dziennego salta daną pseudonimizowaną, a więc nadal osobową (motyw 26 RODO); jej przetwarzanie wymaga podstawy prawnej, a dopuszczalność bez zgody wymaga oceny w każdym przypadku z osobna.
- Brak kontaminacji profili reklamowych: Dzięki codziennemu wygasaniu identyfikatorów statystyczna dokładność raportów jest utrzymana bez wspierania inwazyjnego retargetingu i profilowania użytkowników.
Pytania i odpowiedzi
Na ile niezawodnie skrót rozróżnia odwiedzających, którzy dzielą jedną sieć?
Tylko na tyle, na ile różnią się dane wejściowe. Skrót łączy wszystkie żądania pochodzące z tej samej podsieci /24 i niosące ten sam nagłówek User-Agent, a także ten sam Accept-Language. W sieci firmowej, w hotelowym Wi-Fi albo za NAT-em operatora komórkowego (carrier-grade NAT) wiele osób dzieli jeden adres, a popularne urządzenia z aktualną przeglądarką często wysyłają identyczne ciągi User-Agent. Takie wizyty zlewają się w jedną syntetyczną sesję. Chromium dodatkowo skrócił ciąg User-Agent, więc rozróżnia on mniej niż dawniej.
Z kolei pojedyncza wizyta rozpada się, gdy dane wejściowe zmieniają się po drodze: telefon przechodzący z Wi-Fi do sieci komórkowej dostaje inną podsieć, a aktualizacja przeglądarki zmienia User-Agent. W obu przypadkach w środku wizyty powstaje nowy identyfikator.
Odtworzone sesje są więc oszacowaniem obarczonym błędami w obu kierunkach. Osobna kolumna w BigQuery, proponowana w artykule, pozwala analizować ten udział oddzielnie, na przykład zestawiać współczynniki konwersji sesji z ciasteczkiem i bez niego, zamiast je mieszać.
Dlaczego dziennego salta nie należy zapisywać ani umieszczać w kopiach zapasowych?
Bo tylko on chroni skróty przed odwróceniem. Przestrzeń możliwych danych wejściowych jest mała: istnieje zaledwie około 16,8 miliona podsieci IPv4 o rozmiarze /24, a popularne ciągi User-Agent da się wyliczyć, więc przy znanym salcie każdy identyfikator z danego dnia da się przypisać do podsieci i przeglądarki przez sprawdzanie kolejnych kombinacji. W przykładzie w PHP salt leży w bazie danych jako transient, a jeśli nie działa trwała pamięć podręczna obiektów, w tabeli opcji, a więc w każdej kopii zapasowej bazy, gdzie przetrwa dłużej niż swój dzień.