Difference between revisions for Users / Eo Ny




← Previous edit
Next edit →

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