Difference between revisions for Users / Eo Ny




← Previous edit
Next edit →

Merge of Version1 & Version2
1 == Session Management Technical Documentation ==
2
3 === Overview ===
4
5 The ##Session## class is an abstract session management system for WackoWiki that extends ##ArrayObject## to provide secure, configurable session handling. It implements sophisticated security features including session ID regeneration, anti-replay protection, nonce verification, and user agent/IP validation.
6
7 **Location:** ##src/class/session.php##
8 **Type:** Abstract class (must be extended with a ##SessionStoreInterface## implementation)
9 **Inheritance:** ##ArrayObject##
10
11 ----
12
13 === Table of Contents ===
14   1. ((#core-concepts Core Concepts))
15   2. ((#architecture Architecture))
16   3. ((#configuration Configuration))
17   4. ((#usage Usage))
18   5. ((#security-features Security Features))
19   6. ((#api-reference API Reference))
20   7. ((#session-lifecycle Session Lifecycle))
21   8. ((#flash-data Flash Data))
22   9. ((#nonce-system Nonce System))
23   10. ((#cookie-management Cookie Management))
24   11. ((#error-handling Error Handling))
25   12. ((#implementation-guide Implementation Guide))
26
27 ----
28
29 === Core Concepts ===
30
31 ==== Session State ====
32 The Session class maintains three primary states:
33   - **Inactive** (##$active = false##): Session not yet started or has been closed
34   - **Active** (##$active = true##): Session is running and can store/retrieve data
35   - **Regenerated**: Session ID has been replaced (tracked via ##$regenerated## flag)
36
37 ==== Session Data Storage ====
38 Session data is stored as an array accessible through ##ArrayObject## interface:
39 %%php
40 $session['user_id'] = 123; // Set data
41 echo $session['user_id']; // Get data
42 ```%%
43
44 ### Sticky Data==== Sticky Data ====
45 Variables prefixed with `sticky_`##sticky_## are persistent across session resets:
46 - `sticky__created`  - ##sticky__created##: Session creation timestamp
47 - `sticky__flash`  - ##sticky__flash##: Flash data lifetime tracking
48 - `sticky__log`  - ##sticky__log##: Regeneration event log
49 - `sticky__ip`  - ##sticky__ip##: IP change tracking
50
51 ### Internal Tracking Variables==== Internal Tracking Variables ====
52 Variables prefixed with `__`##__## are internal session metadata:
53 - `__started`  - ##__started##: Session start time
54 - `__updated`  - ##__updated##: Last session update time
55 - `__regenerated`  - ##__regenerated##: Last session ID regeneration time
56 - `__user_agent`  - ##__user_agent##: Client user agent string
57 - `__user_ip`  - ##__user_ip##: Client IP address
58 - `__user_tls`  - ##__user_tls##: TLS/SSL status
59 - `__nonces`  - ##__nonces##: Active nonce storage
60 - `__expire`  - ##__expire##: Session expiration time (for old sessions)
61
62 ----
63
64 ## Architecture=== Architecture ===
65
66 ### Class Hierarchy==== Class Hierarchy ====
67 ```%%
68 ArrayObject (PHP native)
69     ↓
70 Session (abstract)
71     ↓
72 [Concrete Implementation] (must implement store_* methods)
73 ```%%
74
75 ### Key Methods Categories==== Key Methods Categories ====
76
77 **Lifecycle Management:**
78 - `__construct()`  - ##__construct()##: Initialize session object
79 - `start()`  - ##start()##: Begin a session
80 - `write_close()`  - ##write_close()##: Save and close session
81 - `restart()`  - ##restart()##: Destroy and restart session
82 - `terminator()`  - ##terminator()##: Shutdown handler (garbage collection, flash data cleanup)
83
84 **Security:**
85 - `regenerate_id()`  - ##regenerate_id()##: Replace session ID
86 - `verify_nonce()`  - ##verify_nonce()##: Validate nonce tokens
87 - `prevent_replay()`  - ##prevent_replay()##: Anti-replay protection
88 - `create_nonce()`  - ##create_nonce()##: Generate nonce tokens
89
90 **Storage (Abstract - Must Implement):**
91 - `store_open()`  - ##store_open()##: Open session storage
92 - `store_read()`  - ##store_read()##: Read session data
93 - `store_write()`  - ##store_write()##: Write session data
94 - `store_close()`  - ##store_close()##: Close session storage
95 - `store_gc()`  - ##store_gc()##: Garbage collection
96 - `store_validate_id()`  - ##store_validate_id()##: Validate session ID format
97 - `store_generate_id()`  - ##store_generate_id()##: Generate new session ID
98
99 **Cookie Management:**
100 - `setcookie()`  - ##setcookie()##: Set HTTP cookie with security headers
101 - `get_cookie()`  - ##get_cookie()##: Retrieve cookie value
102 - `set_cookie()`  - ##set_cookie()##: Set cookie (legacy interface)
103 - `delete_cookie()`  - ##delete_cookie()##: Remove cookie
104 - `send_cookie()`  - ##send_cookie()##: Internal cookie transmission
105
106 ----
107
108 ## Configuration=== Configuration ===
109
110 ### Configuration Properties (Public)==== Configuration Properties (Public) ====
111
112 All configuration properties are prefixed with `cf_` (config) and can be set before calling `start()`##cf_## (config) and can be set before calling ##start()##:
113
114 #### Session Behavior===== Session Behavior =====
115 ```%%php
116 $session->cf_static = 0; // Disable regenerations (e.g., for CAPTCHA)
117 $session->cf_max_session = 7200; // Max session lifetime (seconds)
118 $session->cf_max_idle = 1440; // Max idle time before destruction (seconds)
119 $session->cf_regen_time = 500; // Seconds between forced ID regenerations
120 $session->cf_regen_probability = 2; // Percentage probability of forced regen (0-100)
121 ```%%
122
123 #### Nonce & Replay Protection===== Nonce & Replay Protection =====
124 ```%%php
125 $session->cf_secret = 'adyaiD9+255JeiskPybgisby'; // Secret for nonce generation
126 $session->cf_nonce_lifetime = 7200; // Nonce expiration (seconds)
127 $session->cf_prevent_replay = 1; // Enable replay attack prevention
128 ```%%
129
130 #### Garbage Collection===== Garbage Collection =====
131 ```%%php
132 $session->cf_gc_probability = 2; // Probability of GC on shutdown (0-100)
133 $session->cf_gc_maxlifetime = 1440; // Max session file lifetime (seconds)
134 ```%%
135
136 #### Cookie Settings===== Cookie Settings =====
137 ```%%php
138 $session->cf_cookie_prefix = ''; // Prefix for all cookies
139 $session->cf_cookie_persistent = false; // Make cookies persistent
140 $session->cf_cookie_lifetime = 0; // Cookie lifetime (0 = session cookie)
143 $session->cf_cookie_secure = false; // HTTPS only
144 $session->cf_cookie_httponly = true; // Disable JavaScript access
145 $session->cf_cookie_samesite = COOKIE_SAMESITE; // SameSite attribute
146 ```%%
147
148 #### Cache Control===== Cache Control =====
149 ```%%php
150 $session->cf_cache_limiter = 'none'; // Cache control mode (public|private|nocache|none)
151 $session->cf_cache_expire = 180*60; // Cache TTL (seconds)
152 $session->cf_cache_mtime = 0; // Modify time for Last-Modified header
153 ```%%
154
155 #### Security Validation===== Security Validation =====
156 ```%%php
157 $session->cf_referer_check = ''; // Check HTTP Referer header
158 ```%%
159
160 #### HTTP Context (Set by HTTP class)===== HTTP Context (Set by HTTP class) =====
161 ```%%php
162 $session->cf_ip; // Client IP address
163 $session->cf_tls; // TLS/SSL connection indicator
164 ```%%
165
166 ----
167
168 ## Usage=== Usage ===
169
170 ### Basic Session Setup==== Basic Session Setup ====
171
172 ```%%php
173 // Create a concrete session implementation
174 class MySession extends Session {
175     // Implement abstract store_* methods
198 $session->write_close();
199
200 // Shutdown handler automatically called via register_shutdown_function()
201 ```%%
202
203 ### Session Data Access==== Session Data Access ====
204
205 ```%%php
206 // Array-like access (via ArrayObject)
207 $session['user_id'] = 123;
208 echo $session['user_id'];
211
212 // Convert to array
213 $all_data = $session->toArray();
214 ```%%
215
216 ### Session ID Management==== Session ID Management ====
217
218 ```%%php
219 // Get current session ID
220 $id = $session->id(); // Returns: e.g., "abc123xyz..."
221
224
225 // Get session ID from request
226 $session->start('myapp', $_REQUEST['sid'] ?? null);
227 ```%%
228
229 ### Session State==== Session State ====
230
231 ```%%php
232 // Check if session is active
233 if ($session->active()) {
234     // Session is running
239
240 // Restart session (destroy old + start new)
241 $session->restart();
242 ```%%
243
244 ----
245
246 ## Security Features=== Security Features ===
247
248 ### 1. Session ID Regeneration==== 1. Session ID Regeneration ====
249
250 **Purpose:** Prevent session fixation attacks
251
252 **Automatic Triggers:**
253 - Initial session creation (`regenerated = 2`  - Initial session creation (##regenerated = 2##)
254 - First request after creation (`regenerated = 1`  - First request after creation (##regenerated = 1##)
255 - Periodic forced regeneration (based on `cf_regen_time` and `cf_regen_probability`  - Periodic forced regeneration (based on ##cf_regen_time## and ##cf_regen_probability##)
256   - Session validation failures
257
258 **Manual Trigger:**
259 ```%%php
260 $session->regenerate_id($delete_old = false, $message = 'custom_reason');
261 ```%%
262
263 **Parameters:**
264 - `$delete_old`  - ##$delete_old##:
265   - `false`##false## (0): Keep old session active for ~5 seconds (for pending AJAX requests)
266   - `true`##true## (1): Keep old session for time specified (unused in current code)
267   - `2`##2##: Immediately destroy old session
268
269 **Implementation Details:**
270 - New session ID is generated via `store_generate_id()`  - New session ID is generated via ##store_generate_id()##
271   - Old session data is copied to new ID
272 - Old session marked with `__expire`  - Old session marked with ##__expire## timestamp
273   - Cookie immediately updated with new ID
274 - Single regeneration per request (checked via `$this->regenerated`  - Single regeneration per request (checked via ##$this->regenerated## flag)
275 - Logged in `sticky__log`  - Logged in ##sticky__log## for debugging (max 15 entries)
276
277 ```%%php
278 // Example: Force regeneration on login
279 $session->start('myapp');
280 if ($user_authenticated) {
281     $session->regenerate_id(false, 'login');
282     $session['user_id'] = $user->id;
283 }
284 ```%%
285
286 ### 2. User Agent Validation==== 2. User Agent Validation ====
287
288 **Purpose:** Detect browser/device changes that might indicate hijacking
289
290 **Behavior:**
291   - Stores user agent on first request
292 - Compares on subsequent requests using `similar_text()`  - Compares on subsequent requests using ##similar_text()##
293   - Destroys session if similarity < 95%
294   - Useful against bot attacks or stolen sessions
295
296 **Configuration:**
297 ```%%php
298 // Automatic on each request (if enabled in code logic)
299 // Triggers session destruction if UA changes significantly
300 ```%%
301
302 ### 3. IP Address Validation==== 3. IP Address Validation ====
303
304 **Purpose:** Detect IP spoofing or hijacking
305
306 **Behavior:**
307   - Stores IP on first request
308   - Compares on subsequent requests
309 - Soft failure on mismatch: `destroy = 1`  - Soft failure on mismatch: ##destroy = 1## (keeps regenerating)
310 - Tracks IP changes in `sticky__ip`  - Tracks IP changes in ##sticky__ip##
311
312 **Configuration:**
313 ```%%php
314 $session->cf_ip = $_SERVER['REMOTE_ADDR']; // Set by HTTP class
315 // Validation happens automatically during start()
316 ```%%
317
318 **IP Change Tracking:**
319 ```%%php
320 // Access IP change history
321 $ip_history = $session->sticky__ip; // Array of [ip => change_count]
322 ```%%
323
324 ### 4. TLS/SSL Validation==== 4. TLS/SSL Validation ====
325
326 **Purpose:** Prevent protocol downgrade attacks
327
328 **Behavior:**
329   - Checks if connection transitioned from HTTPS to HTTP
330   - Destroys session on mismatch
331
332 **Configuration:**
333 ```%%php
334 $session->cf_tls = !empty($_SERVER['HTTPS']); // Set by HTTP class
335 // Validation happens automatically during start()
336 ```%%
337
338 ### 5. Anti-Replay Protection==== 5. Anti-Replay Protection ====
339
340 **Purpose:** Prevent CSRF and replay attacks
341
342 **Mechanism:**
343   - Generates unique "NoReplay" nonce on each request
344   - Cookie-based nonce verification
345   - Detects rapid-fire requests (AJAX attacks)
346
347 **Configuration:**
348 ```%%php
349 $session->cf_prevent_replay = 1; // Enable (default)
350 $session->cf_prevent_replay = 0; // Disable if needed
351 ```%%
352
353 **How It Works:**
354 ```%%
355 Request 1: Generate nonce, send in cookie
356 Request 2: Client sends nonce back, verify & generate new one
357 Request 3: If old nonce used again → reject (replay detected)
358 ```%%
359
360 ### 6. Referer Validation (Optional)==== 6. Referer Validation (Optional) ====
361
362 **Purpose:** Prevent CSRF via header checking
363
364 **Configuration:**
365 ```%%php
366 $session->cf_referer_check = 'example.com';
367 // Session rejected if HTTP_REFERER doesn't contain this string
368 ```%%
369
370 ----
371
372 ## API Reference=== API Reference ===
373
374 ### Public Methods==== Public Methods ====
375
376 #### Lifecycle Management===== Lifecycle Management =====
377
378 ##### `start($name = null, $id = null): bool`====== ##start($name = null, $id = null): bool## ======
379 Start or resume a session.
380
381 **Parameters:**
382 - `$name`  - ##$name## (string|null): Session name (cookie name base). Alphanumeric + underscore/dash. Defaults to 'sesid'
383 - `$id`  - ##$id## (string|null): Existing session ID to resume. If null, attempts to read from cookie
384
385 **Returns:** `true` if session started successfully, `false`##true## if session started successfully, ##false## on error
386
387 **Side Effects:**
388   - Sets headers (cookies, cache control)
389   - Populates session data from storage
390   - Performs security validations
391   - May trigger session ID regeneration
392
393 **Example:**
394 ```%%php
395 if ($session->start('webapp', $_COOKIE['sess_id'] ?? null)) {
396     // Session ready
397 } else {
398     // Session failed
399 }
400 ```%%
401
402 **Validation Steps:**
403   1. Reject if headers already sent
404   2. Validate session name format
405   3. Retrieve ID from parameter or cookie
406   4. Check Referer header (if configured)
407 5. Validate ID format via `store_validate_id()`  5. Validate ID format via ##store_validate_id()##
408   6. Read session data from storage
409   7. Verify nonces and timestamps
410   8. Check user agent, IP, TLS
411   9. Regenerate if needed
412
413 ----
414
415 ##### `write_close(): void`====== ##write_close(): void## ======
416 Save session data and close session.
417
418 **Side Effects:**
419 - Calls `write_session()`  - Calls ##write_session()## to serialize and store data
420 - Calls `store_close()`  - Calls ##store_close()## to close storage handler
421 - Sets `$active = false`  - Sets ##$active = false##
422
423 **Example:**
424 ```%%php
425 $session['key'] = 'value';
426 $session->write_close(); // Ensure data is saved
427 ```%%
428
429 ----
430
431 ##### `restart(): bool`====== ##restart(): bool## ======
432 Destroy current session and create new one.
433
434 **Equivalent to:** `regenerate_id(true) + clean_vars() + populate()`##regenerate_id(true) + clean_vars() + populate()##
435
436 **Returns:** `true` on success, `false`##true## on success, ##false## on error
437
438 **Use Cases:**
439   - User logout and new login
440   - Security reset
441   - Complete session refresh
442
443 **Example:**
444 ```%%php
445 $session->restart();
446 // New session created, old data cleared, sticky_ vars preserved
447 ```%%
448
449 ----
450
451 #### Session Access===== Session Access =====
452
453 ##### `id(): mixed`====== ##id(): mixed## ======
454 Get current session ID.
455
456 **Returns:** Session ID string or null if not started
457
458 ```%%php
459 $sid = $session->id(); // "abc123xyz..."
460 ```%%
461
462 ----
463
464 ##### `name(): string`====== ##name(): string## ======
465 Get session name (cookie prefix).
466
467 **Returns:** Session name
468
469 ```%%php
470 $name = $session->name(); // "myapp"
471 ```%%
472
473 ----
474
475 ##### `active(): bool`====== ##active(): bool## ======
476 Check if session is currently active.
477
478 **Returns:** `true` if session is started and active, `false`##true## if session is started and active, ##false## otherwise
479
480 ```%%php
481 if ($session->active()) {
482     $session['key'] = 'value';
483 }
484 ```%%
485
486 ----
487
488 ##### `message(): string|null`====== ##message(): string|null## ======
489 Get reason for last session state change.
490
491 **Returns:** Message string or null
492
493 **Possible Values:**
494 - `'replay'`  - ##'replay'##: Replay attack detected
495 - `'obsolete'`  - ##'obsolete'##: Session marked for expiration
496 - `'reg_expire'`  - ##'reg_expire'##: Regeneration expiration reached
497 - `'max_session'`  - ##'max_session'##: Max session lifetime exceeded
498 - `'max_idle'`  - ##'max_idle'##: Idle timeout exceeded
499 - `'ua'`  - ##'ua'##: User agent mismatch (>5% difference)
500 - `'tls'`  - ##'tls'##: TLS status changed
501 - `'ip'`  - ##'ip'##: IP address mismatch
502 - `'restart'`  - ##'restart'##: Session manually restarted
503 - `null`  - ##null##: No state change
504
505 **Example:**
506 ```%%php
507 $session->start('app');
508 if ($message = $session->message()) {
509     error_log("Session issue: $message");
510 }
511 ```%%
512
513 ----
514
515 ##### `toArray(): array`====== ##toArray(): array## ======
516 Convert session data to array.
517
518 **Returns:** Associative array of session data
519
520 **Note:** This is a direct call to `ArrayObject::getArrayCopy()`##ArrayObject::getArrayCopy()##
521
522 ```%%php
523 $data = $session->toArray();
524 foreach ($data as $key => $value) {
525     echo "$key => $value\n";
526 }
527 ```%%
528
529 ----
530
531 #### Nonce System===== Nonce System =====
532
533 ##### `create_nonce($action, $expires = null): string`====== ##create_nonce($action, $expires = null): string## ======
534 Generate a unique nonce token.
535
536 **Parameters:**
537 - `$action`  - ##$action## (string): Action identifier (e.g., 'form_submit', 'delete_action')
538 - `$expires` (int|null): Expiration time in seconds. Defaults to `cf_nonce_lifetime`  - ##$expires## (int|null): Expiration time in seconds. Defaults to ##cf_nonce_lifetime##
539
540 **Returns:** Nonce token string (11 characters)
541
542 **Example:**
543 ```%%php
544 $nonce = $session->create_nonce('form_submit', 3600);
545 // Use in HTML: <input type="hidden" name="nonce" value="<?= $nonce ?>">
546 ```%%
547
548 **Storage:**
549 - Stored in `$session->__nonces[]`  - Stored in ##$session->__nonces[]##
550 - Key: `{action}.{base64_encoded_hash}`  - Key: ##{action}.{base64_encoded_hash}##
551   - Value: Expiration timestamp
552
553 ----
554
555 ##### `verify_nonce($action, $code, $protect = 0)`====== ##verify_nonce($action, $code, $protect = 0)## ======
556 Verify a nonce token.
557
558 **Parameters:**
559 - `$action` (string): Action identifier that was used in `create_nonce()`  - ##$action## (string): Action identifier that was used in ##create_nonce()##
560 - `$code`  - ##$code## (string): Nonce token from user
561 - `$protect`  - ##$protect## (int): Protection level
562   - `0`##0##: Single-use nonce (consumed on first verification)
563   - `1+`##1+##: Protected nonce (can verify multiple times, prevents fast replays)
564
565 **Returns:**
566 - `true`  - ##true## (1): Nonce verified and valid
567 - `false`  - ##false## (0): Nonce invalid or expired
568 - `-1`  - ##-1##: Protected nonce used twice in quick succession (possible AJAX attack)
569
570 **Example:**
571 ```%%php
572 if ($nonce = $session->verify_nonce('form_submit', $_POST['nonce'])) {
573     if ($nonce === -1) {
574         // Possible replay, but might be legitimate AJAX
578         process_form();
579     }
580 }
581 ```%%
582
583 **Cleanup:**
584   - Expired nonces automatically removed
585   - Verified single-use nonces removed from storage
586
587 ----
588
589 #### Cookie Management===== Cookie Management =====
590
591 ##### `setcookie($name, $value = null, $expires = 0, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null): bool`====== ##setcookie($name, $value = null, $expires = 0, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null): bool## ======
592 Set a cookie with security headers.
593
594 **Parameters:**
595 - `$name`  - ##$name##: Cookie name (automatically URL-encoded)
596 - `$value`  - ##$value##: Cookie value (automatically URL-encoded, null to delete)
597 - `$expires`  - ##$expires##: Expiration timestamp (0 = session cookie)
598 - `$path`: Cookie path (default: `cf_cookie_path`  - ##$path##: Cookie path (default: ##cf_cookie_path##)
599 - `$domain`: Cookie domain (default: `cf_cookie_domain`  - ##$domain##: Cookie domain (default: ##cf_cookie_domain##)
600 - `$secure`: HTTPS only (default: `cf_cookie_secure`  - ##$secure##: HTTPS only (default: ##cf_cookie_secure##)
601 - `$httponly`: Disable JS access (default: `cf_cookie_httponly`  - ##$httponly##: Disable JS access (default: ##cf_cookie_httponly##)
602 - `$samesite`: SameSite attribute (default: `cf_cookie_samesite`  - ##$samesite##: SameSite attribute (default: ##cf_cookie_samesite##)
603
604 **Returns:** `true` on success, `false`##true## on success, ##false## if headers already sent
605
606 **Features:**
607   - RFC 2616 2.2 token encoding for cookie name
608   - RFC 6265 4.1.1 cookie-octet encoding for value
609   - Removes duplicate cookie headers automatically
610   - Adds all security attributes (secure, httponly, samesite)
611   - Does NOT replace existing cookies (allows multiple Set-Cookie headers)
612
613 **Example:**
614 ```%%php
615 // Session cookie
616 $session->setcookie('user_pref', 'dark_mode');
617
624 // Secure cookie with SameSite
625 $session->setcookie('token', 'abc123', time() + 3600,
626     path: '/', secure: true, httponly: true, samesite: 'Strict');
627 ```%%
628
629 ----
630
631 ##### `get_cookie($name)`====== ##get_cookie($name)## ======
632 Retrieve cookie value.
633
634 **Parameters:**
635 - `$name`  - ##$name##: Cookie name (prefix automatically added)
636
637 **Returns:** Cookie value or null if not set
638
639 ```%%php
640 $value = $session->get_cookie('user_pref'); // Reads $_COOKIE['user_pref']
641 ```%%
642
643 ----
644
645 ##### `set_cookie($name, $value, $persistent = false): void`====== ##set_cookie($name, $value, $persistent = false): void## ======
646 Legacy cookie setter (alternative to `setcookie()`##setcookie()##).
647
648 **Parameters:**
649 - `$name`  - ##$name##: Cookie name (prefix added)
650 - `$value`  - ##$value##: Cookie value
651 - `$persistent`  - ##$persistent##:
652   - `false`##false##: Session cookie (deleted on browser close)
653   - Number: Days to persist
654   - `0`: Use `cf_cookie_persistent`##0##: Use ##cf_cookie_persistent## config
655
656 **Example:**
657 ```%%php
658 $session->set_cookie('theme', 'dark'); // Session cookie
659 $session->set_cookie('lang', 'en', 365); // 1 year
660 ```%%
661
662 ----
663
664 ##### `delete_cookie($name): void`====== ##delete_cookie($name): void## ======
665 Delete a cookie.
666
667 **Parameters:**
668 - `$name`  - ##$name##: Cookie name (prefix added)
669
670 **Implementation:** Sets empty value with immediate expiration
671
672 ```%%php
673 $session->delete_cookie('old_preference');
674 ```%%
675
676 ----
677
678 ##### `unsetcookie($name): void`====== ##unsetcookie($name): void## ======
679 Alias for `setcookie($name)`##setcookie($name)## with no value (convenience method).
680
681 ```%%php
682 $session->unsetcookie('cookie_name');
683 ```%%
684
685 ----
686
687 ### Protected Methods (For Store Implementation)==== Protected Methods (For Store Implementation) ====
688
689 #### `regenerate_id($delete_old = false, $message = ''): bool`===== ##regenerate_id($delete_old = false, $message = ''): bool## =====
690 Internal method to regenerate session ID (called automatically).
691
692 **Protected** - Usually called automatically, but can be overridden/called by subclasses
693
694 ----
695
696 #### `store_generate_id(): string`===== ##store_generate_id(): string## =====
697 Generate a new session ID.
698
699 **Default Implementation:** Returns 21-character random alphanumeric string via `Ut::random_token(21)`##Ut::random_token(21)##
700
701 **Override in subclass to customize:**
702 ```%%php
703 protected function store_generate_id(): string {
704     return hash('sha256', random_bytes(32)); // Your format
705 }
706 ```%%
707
708 ----
709
710 #### `store_validate_id($id): bool`===== ##store_validate_id($id): bool## =====
711 Validate session ID format.
712
713 **Default Implementation:** Regex check: `/^[a-zA-Z\d]{21}$/`##/^[a-zA-Z\d]{21}$/##
714
715 **Override in subclass to match your format:**
716 ```%%php
717 protected function store_validate_id($id): bool {
718     return preg_match('/^[a-f0-9]{64}$/', $id); // SHA256 format
719 }
720 ```%%
721
722 ----
723
724 #### `store_open($name): void`===== ##store_open($name): void## =====
725 Open session storage (called before first read/write).
726
727 **Subclass must implement** - Initialize storage handler
728
729 **Example:**
730 ```%%php
731 protected function store_open($name): void {
732     $this->db = new PDO('sqlite::memory:');
733 }
734 ```%%
735
736 ----
737
738 #### `store_read($id, $lock = false): string|false`===== ##store_read($id, $lock = false): string|false## =====
739 Read session data from storage.
740
741 **Subclass must implement**
742
743 **Parameters:**
744 - `$id`  - ##$id##: Session ID to read
745 - `$lock`  - ##$lock##: If true, lock the session file for writing (create new)
746
747 **Returns:**
748   - Serialized session data (string) if found and locked
749 - Empty string (`''`  - Empty string (##''##) if new session should be created
750 - `false`  - ##false## if session doesn't exist or read error
751
752 **Example:**
753 ```%%php
754 protected function store_read($id, $lock = false): string|false {
755     $data = file_get_contents("/tmp/sess_$id");
756     return $data ?: false;
757 }
758 ```%%
759
760 ----
761
762 #### `store_write($id, $data): void`===== ##store_write($id, $data): void## =====
763 Write session data to storage.
764
765 **Subclass must implement**
766
767 **Parameters:**
768 - `$id`  - ##$id##: Session ID
769 - `$data`: Serialized session data (already processed by `Ut::serialize()`  - ##$data##: Serialized session data (already processed by ##Ut::serialize()##)
770
771 **Example:**
772 ```%%php
773 protected function store_write($id, $data): void {
774     file_put_contents("/tmp/sess_$id", $data);
775 }
776 ```%%
777
778 ----
779
780 #### `store_close(): void`===== ##store_close(): void## =====
781 Close session storage.
782
783 **Subclass must implement** - Release resources
784
785 **Example:**
786 ```%%php
787 protected function store_close(): void {
788     // Close database, file, etc.
789 }
790 ```%%
791
792 ----
793
794 #### `store_gc(): void`===== ##store_gc(): void## =====
795 Perform garbage collection on old sessions.
796
797 **Subclass must implement** - Delete expired sessions
798
799 **Called During:**
800 - Shutdown handler (probabilistic, based on `cf_gc_probability`  - Shutdown handler (probabilistic, based on ##cf_gc_probability##)
801
802 **Should Delete:**
803 - Sessions older than `cf_gc_maxlifetime`  - Sessions older than ##cf_gc_maxlifetime## seconds
804
805 **Example:**
806 ```%%php
807 protected function store_gc(): void {
808     $max_age = time() - $this->cf_gc_maxlifetime;
809     // Delete files/records older than $max_age
810 }
811 ```%%
812
813 ----
814
815 ### Private Methods (Internal Use)==== Private Methods (Internal Use) ====
816
817 ##### `populate(): void`====== ##populate(): void## ======
818 Initialize session tracking variables on first request.
819
820 **Called by:** `start()`, `restart()`##start()##, ##restart()##
821
822 **Initializes:**
823 - `__started`  - ##__started##: Current timestamp
824 - `__regenerated`  - ##__regenerated##: Current timestamp
825 - `__user_agent`  - ##__user_agent##: Browser user agent
826 - `__user_ip`  - ##__user_ip##: Client IP (if configured)
827 - `__user_tls`  - ##__user_tls##: TLS status (if configured)
828 - `sticky__created`  - ##sticky__created##: Creation time (if not exists)
829
830 ----
831
832 ##### `write_session(): void`====== ##write_session(): void## ======
833 Serialize and write session data to storage.
834
835 **Called by:** `regenerate_id()`, `write_close()`, `terminator()`##regenerate_id()##, ##write_close()##, ##terminator()##
836
837 **Updates:**
838 - `__updated`  - ##__updated##: Current timestamp
839 - Calls `store_write()`  - Calls ##store_write()## with serialized data
840
841 ----
842
843 ##### `clean_vars(): void`====== ##clean_vars(): void## ======
844 Remove non-sticky session variables.
845
846 **Called by:** `restart()`##restart()##, session validation failure
847
848 **Preserves:** Variables starting with `sticky_`##sticky_##
849
850 ----
851
852 ##### `prevent_replay(): void`====== ##prevent_replay(): void## ======
853 Generate and send anti-replay nonce.
854
855 **Called by:** `populate()`##populate()##
856
857 **Action:**
858   - Creates 'NoReplay' nonce
859 - Sends in cookie: `{cf_cookie_prefix}NoReplay`  - Sends in cookie: ##{cf_cookie_prefix}NoReplay##
860
861 ----
862
863 ##### `cache_limiter(): void`====== ##cache_limiter(): void## ======
864 Set HTTP cache control headers based on configuration.
865
866 **Called by:** `start()`##start()## after session data loaded
867
868 **Modes:**
869 - `'public'`: Cacheable, `Cache-Control: public, max-age=...`  - ##'public'##: Cacheable, ##Cache-Control: public, max-age=...##
870 - `'private'`: Private, `Cache-Control: private, max-age=...`  - ##'private'##: Private, ##Cache-Control: private, max-age=...##
871 - `'private_no_expire'`  - ##'private_no_expire'##: Private no TTL
872 - `'nocache'`: No storage, `Cache-Control: no-store`  - ##'nocache'##: No storage, ##Cache-Control: no-store##
873 - `'none'`  - ##'none'##: No headers (default)
874
875 ----
876
877 ##### `set_new_id(): void`====== ##set_new_id(): void## ======
878 Generate and assign new session ID, send in cookie.
879
880 **Called by:** `regenerate_id()`, `start()`##regenerate_id()##, ##start()## (for new sessions)
881
882 ----
883
884 ##### `remove_cookie($cookie): void`====== ##remove_cookie($cookie): void## ======
885 Remove existing Set-Cookie header to avoid duplicates.
886
887 **Called by:** `setcookie()`##setcookie()## before setting new value
888
889 ----
890
891 ##### `nonce_index($action, $code): string` (static)====== ##nonce_index($action, $code): string## (static) ======
892 Generate storage key for nonce.
893
894 **Returns:** `{action}.{base64_encoded_hash}`##{action}.{base64_encoded_hash}##
895
896 ----
897
898 ----
899
900 ## Session Lifecycle=== Session Lifecycle ===
901
902 ### Complete Session Flow==== Complete Session Flow ====
903
904 ```%%
905 ┌─ Browser Request
906
907 ├─ Application Code
954       │ └─ store_gc() (cf_gc_probability % chance)
955       │ └─ Delete old sessions
956       └─ Output sent to browser
957 ```%%
958
959 ### First Request (New Session)==== First Request (New Session) ====
960
961 ```%%
962 start() is called
963 ├─ No ID in cookie
964 ├─ store_read(id) → false
973 │ ├─ __user_agent = UA
974 │ └─ sticky__created = now
975 └─ return true
976 ```%%
977
978 ### Subsequent Request (Resume Session)==== Subsequent Request (Resume Session) ====
979
980 ```%%
981 start() is called
982 ├─ ID from cookie
983 ├─ store_read(id) → serialized_data
990 │ ├─ UA/IP/TLS checks
991 │ └─ May trigger regenerate_id()
992 └─ return true
993 ```%%
994
995 ### Session ID Regeneration==== Session ID Regeneration ====
996
997 ```%%
998 regenerate_id($delete_old, $message) is called
999 ├─ Check not headers_sent()
1000 ├─ Check $active
1012 ├─ Set: regenerated = 1
1013 ├─ Log event: sticky__log[] = [now, message]
1014 └─ return true
1015 ```%%
1016
1017 ### Session Destruction==== Session Destruction ====
1018
1019 ```%%
1020 Triggered by:
1021 ├─ restart() → regenerate_id(true)
1022 ├─ Validation failure (destroy=2)
1028 ├─ Non-sticky variables cleared
1029 ├─ sticky_ variables preserved
1030 └─ New session ID generated
1031 ```%%
1032
1033 ----
1034
1035 ## Flash Data=== Flash Data ===
1036
1037 Flash data persists for a limited number of requests (typically 1-2) and is automatically removed.
1038
1039 ### Usage==== Usage ====
1040
1041 ```%%php
1042 // Store flash message for next request
1043 $session->set_flash('error', 'Username already exists', 1); // 1 request
1044 $session->set_flash('info', 'Welcome back!', 2); // 2 requests
1045
1046 // In next request, data automatically available
1047 echo $session['error']; // "Username already exists"
1048 %%
1049
1050 ==== How It Works ====
1051   1. **Storage:** Flash data stored in ##$session->sticky__flash##
1052   - Key: Variable name
1053   - Value: Lifetime in requests
1054   2. **Cleanup:** In ##terminator()## (shutdown handler):
1055    %%php
1056    foreach ($sticky__flash as $var => $age) {
1057        if (!isset($session[$var])) {
1058            unset($sticky__flash[$var]); // Already deleted
1063            $flash__flash[$var] = $age; // Decrement counter
1064        }
1065    }
1066    %%
1067   3. **Persistence:** Flash variables are kept in ##sticky__flash## even during session resets
1068
1069 ==== Example: Login Flow ====
1070
1071 %%php
1072 // POST /login
1073 if ($credentials_valid) {
1074     $session->restart(); // New session
1087 if ($message = $session['success'] ?? null) {
1088     echo "<div class='success'>$message</div>";
1089 }
1090 ```%%
1091
1092 ----
1093
1094 ## Nonce System=== Nonce System ===
1095
1096 Nonces provide CSRF protection and replay attack detection.
1097
1098 ==== Terminology ====
1099   - **Nonce:** Number used ONCE - cryptographic token for action verification
1100   - **Action:** Type of operation being protected (e.g., 'form_submit', 'delete_user')
1101   - **Protected Nonce:** Can be verified multiple times with protection against rapid reuse
1102
1103 ==== Complete Example: Form Protection ====
1104
1105 %%php
1106 // 1. Display form with nonce
1107 $nonce = $session->create_nonce('user_update', 3600);
1108 ?>
1123     // Safe to process
1124     update_user($_POST);
1125 }
1126 ```%%
1127
1128 ### Example: Protected Nonce (AJAX-Safe)==== Example: Protected Nonce (AJAX-Safe) ====
1129
1130 ```%%php
1131 // Generate protected nonce (can verify multiple times)
1132 $nonce = $session->create_nonce('ajax_action', 300);
1133
1148     // Safe to process
1149     process_ajax();
1150 }
1151 ```%%
1152
1153 ### Nonce Storage Format==== Nonce Storage Format ====
1154
1155 ```%%
1156 Internal storage (__nonces array):
1157 [
1158     "{action}.{hash}" => expiration_timestamp,
1163 - action: Custom action identifier
1164 - hash: First 11 chars of base64(sha1(code_bytes))
1165 - expiration_timestamp: time() + lifetime
1166 %%
1167
1168 ==== Security Properties ====
1169   - **CSRF Protection:** Nonce must match to process form
1170   - **One-Time Use:** Each nonce consumed after first verification (unless protected)
1171   - **Expiration:** Nonces automatically expire
1172   - **Action-Specific:** Each action has separate nonce space
1173   - **AJAX-Safe:** Protected nonces allow multiple quick verifications
1174
1175 ----
1176
1177 === Cookie Management ===
1178
1179 ==== Security Features ====
1180
1181 The ##setcookie()## method implements comprehensive cookie security:
1182
1183 ===== Encoding =====
1184 %%php
1185 // Cookie names: RFC 2616 2.2 token format
1186 // Cookie values: RFC 6265 4.1.1 cookie-octet format
1187 // Unsafe characters automatically URL-encoded
1188 ```%%
1189
1190 #### Security Attributes===== Security Attributes =====
1191 ```%%php
1192 setcookie('auth', 'token',
1193     expires: time() + 3600,
1194     secure: true, // HTTPS only
1195     httponly: true, // Disable JavaScript
1196     samesite: 'Strict' // CSRF protection
1197 );
1198 ```%%
1199
1200 #### No Duplicate Headers===== No Duplicate Headers =====
1201 ```%%php
1202 // Automatically removes old Set-Cookie header before setting new one
1203 // Prevents cookie header duplication
1204 remove_cookie($name) → clears old headers
1205 setcookie() → sets new header
1206 ```%%
1207
1208 ### Configuration-Driven Defaults==== Configuration-Driven Defaults ====
1209
1210 ```%%php
1211 $session->cf_cookie_path = '/app'; // Path
1212 $session->cf_cookie_domain = '.example.com'; // Domain
1213 $session->cf_cookie_secure = true; // HTTPS
1217
1218 $session->setcookie('token', 'value');
1219 // Uses all configured defaults
1220 ```%%
1221
1222 ### Typical Secure Configuration==== Typical Secure Configuration ====
1223
1224 ```%%php
1225 // Prevent XSS and CSRF
1226 $session->cf_cookie_secure = true; // HTTPS only
1227 $session->cf_cookie_httponly = true; // No JavaScript access
1234 // Session cookies (delete on browser close)
1235 $session->cf_cookie_lifetime = 0;
1236 $session->cf_cookie_persistent = false;
1237 ```%%
1238
1239 ----
1240
1241 ## Error Handling=== Error Handling ===
1242
1243 ### Graceful Degradation==== Graceful Degradation ====
1244
1245 The Session class gracefully handles errors:
1246
1247 #### Headers Already Sent===== Headers Already Sent =====
1248 ```%%php
1249 if (headers_sent($file, $line)) {
1250     trigger_error("id regeneration requested after headers flushed at $file:$line",
1251                   E_USER_WARNING);
1252     return false;
1253 }
1254 ```%%
1255
1256 **Impact:** Session ID cannot be regenerated, but session continues
1257
1258 #### Cookie Setting Failure===== Cookie Setting Failure =====
1259 ```%%php
1260 if (headers_sent($file, $line)) {
1261     trigger_error("cannot place session cookie $name=$value due to $file:$line",
1262                   E_USER_WARNING);
1263     return;
1264 }
1265 ```%%
1266
1267 **Impact:** Cookie not set, but session data remains accessible
1268
1269 #### Storage Errors===== Storage Errors =====
1270 ```%%php
1271 if ($this->store_read($this->id, true) !== '') {
1272     // error! [comment indicates error, but continues]
1273 }
1274 ```%%
1275
1276 **Impact:** Creates new session if storage returns error
1277
1278 ### Debug Logging==== Debug Logging ====
1279
1280 The Session class includes commented debug statements:
1281
1282 ```%%php
1283 # Ut::dbg("regeneration failed by flush at $file:$line");
1284 # Ut::dbg($destroy, $message);
1285 # Ut::dbg("session setcookie $name failed by $file:$line");
1286 ```%%
1287
1288 To enable: Uncomment lines and ensure `Ut::dbg()`##Ut::dbg()## function exists
1289
1290 ### Event Logging==== Event Logging ====
1291
1292 Session events tracked in `sticky__log`##sticky__log##:
1293
1294 ```%%php
1295 // Access session event history
1296 if (isset($session->sticky__log)) {
1297     foreach ($session->sticky__log as [$timestamp, $message]) {
1298         echo "[$timestamp] $message\n";
1299     }
1300 }
1301 ```%%
1302
1303 **Logged Events:**
1304   - Session regeneration (with reason)
1305   - Limited to 15 most recent events (old entries archived as '...')
1306
1307 ----
1308
1309 ## Implementation Guide=== Implementation Guide ===
1310
1311 ### Creating a Concrete Session Class==== Creating a Concrete Session Class ====
1312
1313 You must implement the abstract storage methods. Choose your storage backend: files, database, cache, etc.
1314
1315 #### File-Based Storage===== File-Based Storage =====
1316
1317 ```%%php
1318 <?php
1319
1320 class FileSession extends Session {
1371         }
1372     }
1373 }
1374 ```%%
1375
1376 #### Database Storage (PDO)===== Database Storage (PDO) =====
1377
1378 ```%%php
1379 <?php
1380
1381 class DatabaseSession extends Session {
1439                    ->execute([$cutoff]);
1440     }
1441 }
1442 ```%%
1443
1444 #### Redis Storage===== Redis Storage =====
1445
1446 ```%%php
1447 <?php
1448
1449 class RedisSession extends Session {
1489         // Redis handles expiration automatically with TTL
1490     }
1491 }
1492 ```%%
1493
1494 ### Complete Integration Example==== Complete Integration Example ====
1495
1496 ```%%php
1497 <?php
1498
1499 // Initialize session with configuration
1539 }
1540
1541 // Automatic cleanup happens in register_shutdown_function()
1542 ```%%
1543
1544 ### Configuration Best Practices==== Configuration Best Practices ====
1545
1546 ```%%php
1547 <?php
1548
1549 class SessionConfig {
1580 $session = new FileSession();
1581 SessionConfig::apply($session, $_ENV['APP_ENV'] ?? 'production');
1582 $session->start('myapp');
1583 ```%%
1584
1585 ### Testing Tips==== Testing Tips ====
1586
1587 ```%%php
1588 <?php
1589
1590 // Test nonce generation and verification
1612 $session2 = new FileSession();
1613 $session2->start('myapp');
1614 assert($session2['test_key'] === 'test_value');
1615 ```%%
1616
1617 ----
1618
1619 ## Security Checklist=== Security Checklist ===
1620
1621 Use this checklist when implementing sessions:
1622   - [ ] Use HTTPS only in production
1623   - [ ] Enable ##cf_cookie_secure##
1624   - [ ] Enable ##cf_cookie_httponly##
1625   - [ ] Set ##cf_cookie_samesite## to 'Strict' or 'Lax'
1626   - [ ] Set appropriate ##cf_max_session## timeout
1627   - [ ] Set appropriate ##cf_max_idle## timeout
1628   - [ ] Enable ##cf_prevent_replay##
1629   - [ ] Validate ##cf_ip## if possible
1630   - [ ] Validate ##cf_tls## on HTTPS sites
1631   - [ ] Use nonces for all state-changing forms
1632   - [ ] Implement proper logout (call ##restart()##)
1633   - [ ] Regenerate on privilege escalation (login)
1634   - [ ] Monitor ##sticky__ip## for suspicious changes
1635   - [ ] Review ##sticky__log## for attack patterns
1636   - [ ] Implement garbage collection (##store_gc##)
1637   - [ ] Hash session IDs before storing (see TODOs)
1638   - [ ] Use secure random token generation
1639
1640 ----
1641
1642 === Common Patterns ===
1643
1644 ==== Login Flow ====
1645
1646 %%php
1647 if ($_POST['action'] === 'login') {
1648     $user = authenticate($_POST['username'], $_POST['password']);
1649     if ($user) {
1657         header('Location: /login');
1658     }
1659 }
1660 ```%%
1661
1662 ### Logout Flow==== Logout Flow ====
1663
1664 ```%%php
1665 if ($_GET['action'] === 'logout') {
1666     $session->restart(); // Complete reset
1667     header('Location: /');
1668 }
1669 ```%%
1670
1671 ### CSRF-Protected Form==== CSRF-Protected Form ====
1672
1673 ```%%php
1674 // Display form
1675 $csrf = $session->create_nonce('form_' . $form_id, 3600);
1676 echo '<form method="POST">';
1685     }
1686     // Process safely
1687 }
1688 ```%%
1689
1690 ### Permission Check with Session Regeneration==== Permission Check with Session Regeneration ====
1691
1692 ```%%php
1693 if ($user->privilege_level < ADMIN_LEVEL && $promoted_to_admin) {
1694     $session->regenerate_id(false, 'privilege_escalation');
1695     $session['is_admin'] = true;
1696 }
1697 ```%%
1698
1699 ### Session Messages/Flash==== Session Messages/Flash ====
1700
1701 ```%%php
1702 // After action
1703 $session->set_flash('info', 'Profile updated successfully', 1);
1704
1706 if (isset($session['info'])) {
1707     echo $session['info'];
1708 }
1709 %%
1710
1711 ----
1712
1713 === Performance Considerations ===
1714
1715 ==== Optimization Tips ====
1716   1. **Minimize Session Writes:**
1717   - Session data only written during ##write_close()## or regeneration
1718   - No unnecessary serialization during reads
1719   2. **Garbage Collection:**
1720   - Probabilistic GC (based on ##cf_gc_probability##)
1721   - Only runs on ~2% of requests by default
1722   - Customize based on your session volume
1723   3. **Nonce Cleanup:**
1724   - Expired nonces automatically removed on verification
1725   - Verified nonces removed from storage
1726   - No manual cleanup needed
1727   4. **Session ID Validation:**
1728   - Regex-based validation is fast
1729   - No database lookup needed
1730   5. **Caching Strategy:**
1731   - Cache expensive lookups between session operations
1732   - Session data loaded once per request
1733
1734 ==== Benchmarks ====
1735
1736 Typical performance on modern hardware:
1737   - Session start: ~1-5ms (file) / ~2-10ms (database)
1738   - Session write: <1ms (file) / 1-5ms (database)
1739   - Nonce generation: <1ms
1740   - Nonce verification: <1ms
1741
1742 ----
1743
1744 === Troubleshooting ===
1745
1746 ==== Session Not Starting ====
1747
1748 %%php
1749 if (!$session->start('myapp')) {
1750     // Check reasons:
1751     // 1. Headers already sent?
1753     // 3. Permissions issue on session directory?
1754     debug_backtrace();
1755 }
1756 ```%%
1757
1758 ### Cookie Not Setting==== Cookie Not Setting ====
1759
1760 ```%%php
1761 // If setcookie() returns false:
1762 // - Check if headers_sent()
1763 // - Check if cookie name is RFC 2616 compliant
1764 // - Check if cookie value is properly encoded
1765 ```%%
1766
1767 ### Session ID Not Regenerating==== Session ID Not Regenerating ====
1768
1769 ```%%php
1770 // If regenerate_id() returns false:
1771 // - Headers might be sent
1772 // - $active might be false
1774 if (!$session->regenerate_id()) {
1775     error_log("Regeneration failed: headers sent or session inactive");
1776 }
1777 ```%%
1778
1779 ### Nonce Verification Failing==== Nonce Verification Failing ====
1780
1781 ```%%php
1782 // If verify_nonce() returns false:
1783 // 1. Nonce might be expired
1784 // 2. Nonce might be for different action
1787
1788 // Debug:
1789 var_dump($session->__nonces); // See stored nonces
1790 ```%%
1791
1792 ### Session Data Lost==== Session Data Lost ====
1793
1794 ```%%php
1795 // Possible causes:
1796 // 1. write_close() not called (usually automatic via shutdown)
1797 // 2. Storage backend failing silently
1802 if ($message = $session->message()) {
1803     error_log("Session issue: $message");
1804 }
1805 ```%%
1806
1807 ----
1808
1809 ## TODO Items (From Code Comments)=== TODO Items (From Code Comments) ===
1810
1811 The following improvements are planned:
1812   1. **Do not store session ID in filename or DB index - store hash instead**
1813   - Improves security by not exposing IDs in storage layer
1814   - Would require hashing logic in store_* methods
1815   2. **Log of IP changes and other possible security alerts**
1816   - Track ##sticky__ip## changes more comprehensively
1817   - Create security audit trail
1818   3. **Allocate internal unique session which lives through lifetime of uber-session**
1819   - Multi-session management (parent/child sessions)
1820   - Useful for complex user flows
1821   4. **Do not delete old sessions, but use them as hijack pointers**
1822   - Maintain session history for analysis
1823   - Detect potential session hijacking patterns
1824   - Implement session relationship tracking
1825   5. **All SIDs used later than ~5secs of regenerations is hijacks**
1826   - Detect and block delayed session ID usage
1827   - Current implementation allows 5-second window
1828   - Could be more granular
1829
1830 ----
1831
1832 === References ===
1833
1834 ==== Security Standards ====
1835   - RFC 2616: HTTP/1.1 (Cookie syntax)
1836   - RFC 6265: HTTP State Management Mechanism
1837   - RFC 6234: US Secure Hash and Message Authentication Code Algorithms
1838   - OWASP: Session Management Cheat Sheet
1839   - OWASP: Cross-Site Request Forgery (CSRF) Prevention
1840
1841 ==== Related Code ====
1842   - ##Ut::serialize()## / ##Ut::unserialize()##: Session data serialization
1843   - ##Ut::random_token()##: Cryptographic token generation
1844   - ##Ut::http_date()##: HTTP date formatting
1845   - ##Ut::urlencode()##: Cookie-safe encoding
1846   - ##Ut::is_empty()##: Empty value checking
1847
1848 ==== See Also ====
1849   - ##src/class/http.php##: HTTP request/response handling
1850   - ##src/class/auth.php##: Authentication (uses Session)
1851   - Session security best practices in OWASP documentation
1852
1853 ----
1854
1855 === Version History ===
1856   - **Current**: Abstract session class with security features
1857   - **Planned**: Implementation of TODO items above
1858
1859 ----
1860   *Documentation generated: 2026-05-05*
1861   *For latest updates, see: https://github.com/Trojer/wackowiki/blob/main/docs/SESSION_DOCUMENTATION.md*