# CHIPS Cookies (Cookies Having Independent Partitioned State) CHIPS Cookie-Unterstützung im Custom PHP Framework für datenschutzfreundliche Third-Party-Cookies. ## Übersicht CHIPS (Cookies Having Independent Partitioned State) ist ein Browser-Security-Feature, das Third-Party-Cookies **pro Top-Level-Site partitioniert**, um Cross-Site-Tracking zu verhindern. **Browser-Support**: - Chrome 114+ - Edge 114+ - Opera 100+ - Safari 16.4+ (partial) ## Was ist CHIPS? ### Problem: Traditional Third-Party Cookies ``` Widget embedded on example.com: cookie=user123 Same widget on test.com: cookie=user123 (SAME COOKIE!) → Cross-Site-Tracking möglich ``` ### Lösung: CHIPS Partitioned Cookies ``` Widget embedded on example.com: cookie=user123_site_A Same widget on test.com: cookie=user456_site_B (DIFFERENT!) → Kein Cross-Site-Tracking ``` ## Requirements CHIPS Cookies erfordern: 1. **Secure=true** (HTTPS only) 2. **SameSite=None** (Third-Party context) 3. **Partitioned** Attribut ## Verwendung ### CookieService Injection ```php final readonly class EmbeddedWidgetController { public function __construct( private CookieService $cookieService ) {} } ``` ### Standard Cookie vs. CHIPS Cookie ```php // ❌ Standard Third-Party Cookie (tracking möglich) $cookie = $this->cookieService->create( name: 'analytics', value: 'user123', sameSite: SameSite::None, secure: true ); // ✅ CHIPS Partitioned Cookie (tracking verhindert) $cookie = $this->cookieService->createPartitioned( name: 'analytics', value: 'user123' ); ``` ### Use Case 1: Embedded Chat Widget ```php #[Route(path: '/widget/chat/init', method: Method::GET)] public function initChatWidget(HttpRequest $request): JsonResponse { $session = $this->chatService->createSession(); // Partitioned session cookie - different per embedding site $sessionCookie = $this->cookieService->createPartitionedSession( name: 'chat_session', value: $session->id, httpOnly: true ); // Partitioned state cookie - JavaScript accessible $stateCookie = $this->cookieService->createPartitioned( name: 'chat_state', value: json_encode(['minimized' => false]), maxAge: Duration::fromDays(7), httpOnly: false // JS needs access ); return (new JsonResponse(['session_id' => $session->id])) ->withCookies($sessionCookie, $stateCookie); } ``` ### Use Case 2: Third-Party Analytics ```php #[Route(path: '/analytics/track', method: Method::POST)] public function track(HttpRequest $request): JsonResponse { $userId = $request->cookies->get('_analytics'); if (!$userId) { // Create partitioned analytics cookie $userId = $this->generateUserId(); $cookie = $this->cookieService->createAnalyticsCookie( userId: $userId, maxAge: Duration::fromDays(365) ); return (new JsonResponse(['tracked' => true])) ->withCookie($cookie); } $this->analyticsService->track($userId, $request->parsedBody); return new JsonResponse(['tracked' => true]); } ``` ### Use Case 3: Payment Provider Embed ```php #[Route(path: '/payment/stripe/embed', method: Method::GET)] public function embedPaymentForm(HttpRequest $request): ViewResult { $checkoutSession = $this->stripeService->createCheckoutSession(); // Partitioned - separate state per merchant site $sessionCookie = $this->cookieService->createPartitioned( name: 'stripe_checkout', value: $checkoutSession->id, maxAge: Duration::fromMinutes(30), httpOnly: true ); return (new ViewResult('payment/stripe-embed', [ 'session' => $checkoutSession ]))->withCookie($sessionCookie); } ``` ## CookieService Factory Methods ### Standard Cookies ```php // Basic cookie $cookie = $this->cookieService->create( name: 'session', value: 'abc123', maxAge: Duration::fromHours(1), path: '/', domain: null, secure: null, // Auto true in production httpOnly: true, sameSite: SameSite::Lax ); // Session cookie (no expiration) $cookie = $this->cookieService->createSessionCookie( name: 'session_id', value: 'xyz789', httpOnly: true, sameSite: SameSite::Lax ); // Remember-me cookie (30 days default) $cookie = $this->cookieService->createRememberMeCookie( value: 'token123', maxAge: Duration::fromDays(30) // Optional ); // Delete cookie $cookie = $this->cookieService->delete('session', path: '/'); ``` ### CHIPS Partitioned Cookies ```php // Partitioned cookie $cookie = $this->cookieService->createPartitioned( name: 'widget', value: 'state123', maxAge: Duration::fromHours(1), path: '/', domain: null, httpOnly: true ); // Partitioned session cookie $cookie = $this->cookieService->createPartitionedSession( name: 'widget_session', value: 'session123', httpOnly: true ); // Partitioned analytics cookie (JS accessible) $cookie = $this->cookieService->createAnalyticsCookie( userId: 'user123', maxAge: Duration::fromDays(365) ); // Partitioned widget state cookie $cookie = $this->cookieService->createWidgetStateCookie( widgetId: 'chat', state: json_encode(['minimized' => false]), maxAge: Duration::fromDays(30) ); ``` ## Response Integration ### Single Cookie ```php return (new JsonResponse(['success' => true])) ->withCookie($cookie); ``` ### Multiple Cookies ```php return (new JsonResponse(['success' => true])) ->withCookies($sessionCookie, $stateCookie); ``` ## Validation & Helpers ```php // Validate cookie size (4KB browser limit) if (!$this->cookieService->validateSize($cookie)) { throw new \RuntimeException('Cookie exceeds size limit'); } // Validate cookie name if (!$this->cookieService->isValidName('user-token')) { throw new \InvalidArgumentException('Invalid cookie name'); } ``` ## Cookie Header Output ### Standard Cookie ```http Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax ``` ### CHIPS Partitioned Cookie ```http Set-Cookie: widget=state123; Path=/; Secure; HttpOnly; SameSite=None; Partitioned ``` ## Wann CHIPS verwenden? ### ✅ Use CHIPS für: 1. **Embedded Widgets** - Chat, Support, Social Media embeds 2. **Third-Party Analytics** - Analytics in iframes 3. **Payment Provider Embeds** - Stripe, PayPal checkout forms 4. **OAuth Flows in iframes** - Third-party authentication 5. **Cross-Origin Video Players** - Embedded media players ### ❌ NICHT CHIPS für: 1. **First-Party Session Cookies** - SameSite=Lax reicht 2. **Authentication Cookies** - Nur same-site verwenden 3. **CSRF Tokens** - SameSite=Strict empfohlen 4. **Standard E-Commerce** - First-party cookies ausreichend ## Security Best Practices ### 1. Production Environment ```php // Secure flag wird in production automatisch gesetzt $prodEnv = new Environment([EnvKey::APP_ENV->value => 'production']); $service = new CookieService($prodEnv); $cookie = $service->create('session', 'value'); // → Secure flag automatisch true in production ``` ### 2. HTTPS Required CHIPS Cookies erfordern HTTPS: ```php // ✅ Wird in Cookie-Konstruktor validiert $cookie = new Cookie( name: 'widget', value: 'data', secure: true, // Required sameSite: SameSite::None, // Required partitioned: true ); // ❌ Throws InvalidArgumentException $cookie = new Cookie( name: 'widget', value: 'data', secure: false, // Invalid! partitioned: true ); ``` ### 3. HttpOnly für Session Cookies ```php // ✅ HttpOnly für session cookies $session = $this->cookieService->createPartitionedSession( name: 'session', value: 'secret', httpOnly: true // XSS protection ); // ❌ Nur wenn JS-Zugriff WIRKLICH notwendig $widget = $this->cookieService->createPartitioned( name: 'widget_state', value: json_encode(['data']), httpOnly: false // JS accessible ); ``` ## Testing ```php describe('CHIPS Cookie Integration', function () { it('creates partitioned cookie for widget', function () { $service = new CookieService($this->environment); $cookie = $service->createPartitioned( name: 'widget', value: 'state123' ); expect($cookie->partitioned)->toBeTrue(); expect($cookie->secure)->toBeTrue(); expect($cookie->sameSite)->toBe(SameSite::None); $header = $cookie->toHeaderString(); expect($header)->toContain('Partitioned'); }); }); ``` ## Framework Integration ### DI Container Initializer ```php final readonly class CookieServiceInitializer { #[Initializer] public function __invoke(Container $container): CookieService { return new CookieService( environment: $container->get(Environment::class) ); } } ``` ### Response Trait (Future Enhancement) ```php trait ResponseWithCookies { private array $cookies = []; public function withCookie(Cookie $cookie): self { $new = clone $this; $new->cookies[] = $cookie; return $new; } public function withCookies(Cookie ...$cookies): self { $new = clone $this; $new->cookies = array_merge($new->cookies, $cookies); return $new; } } ``` ## Migration von Standard Third-Party Cookies ### Before (Traditional) ```php $cookie = new Cookie( name: 'analytics', value: 'user123', secure: true, sameSite: SameSite::None ); // Problem: Cross-site tracking möglich ``` ### After (CHIPS) ```php $cookie = $this->cookieService->createPartitioned( name: 'analytics', value: 'user123' ); // Benefit: Privacy-friendly, kein Cross-site tracking ``` ## Browser Compatibility Check (Future) ```php final readonly class ChipsSupportDetector { public function supportsChips(UserAgent $userAgent): bool { // Chrome/Edge 114+, Safari 16.4+, Opera 100+ return $this->detectBrowserVersion($userAgent); } public function getFallbackStrategy(): CookieStrategy { // First-party only, localStorage, etc. return CookieStrategy::FIRST_PARTY_ONLY; } } ``` ## Troubleshooting ### Cookie wird nicht gesetzt 1. **HTTPS fehlt**: CHIPS erfordert Secure=true → HTTPS 2. **SameSite≠None**: Muss `SameSite::None` sein 3. **Browser-Support**: Prüfe Browser-Version 4. **Cookie-Size**: Max 4KB - nutze `validateSize()` ### Cross-Site funktioniert nicht 1. **Partitioned fehlt**: Nutze `createPartitioned()` statt `create()` 2. **Domain-Mismatch**: Domain-Attribut korrekt setzen 3. **Path-Mismatch**: Path muss übereinstimmen ## Performance Considerations - **Cookie-Size**: CHIPS Cookies zählen zum 4KB-Limit - **Anzahl Cookies**: Browser-Limit beachten (typically 50-180) - **Expiration**: Alte Cookies regelmäßig löschen ## Zusammenfassung CHIPS Cookies bieten: ✅ **Privacy-Friendly** - Kein Cross-Site-Tracking ✅ **Framework-Compliant** - Value Objects, Readonly, Type Safety ✅ **Developer-Friendly** - Einfache Factory Methods ✅ **Production-Ready** - Automatic Secure Flag, Validation ✅ **Well-Tested** - Comprehensive Pest Test Suite Das Framework unterstützt CHIPS nativ für moderne, datenschutzfreundliche Third-Party-Integrationen.