feat: Add CSRF middleware and performance index migrations
- Create CsrfMiddleware.php with token generation and validation - Add database indexes for ticket_comments and audit_log - Includes rollback script for safe deployment Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
55
middleware/CsrfMiddleware.php
Normal file
55
middleware/CsrfMiddleware.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CSRF Protection Middleware
|
||||||
|
* Generates and validates CSRF tokens for all state-changing operations
|
||||||
|
*/
|
||||||
|
class CsrfMiddleware {
|
||||||
|
private static $tokenName = 'csrf_token';
|
||||||
|
private static $tokenTime = 'csrf_token_time';
|
||||||
|
private static $tokenLifetime = 3600; // 1 hour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new CSRF token
|
||||||
|
*/
|
||||||
|
public static function generateToken() {
|
||||||
|
$_SESSION[self::$tokenName] = bin2hex(random_bytes(32));
|
||||||
|
$_SESSION[self::$tokenTime] = time();
|
||||||
|
return $_SESSION[self::$tokenName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current CSRF token, regenerate if expired
|
||||||
|
*/
|
||||||
|
public static function getToken() {
|
||||||
|
if (!isset($_SESSION[self::$tokenName]) || self::isTokenExpired()) {
|
||||||
|
return self::generateToken();
|
||||||
|
}
|
||||||
|
return $_SESSION[self::$tokenName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate CSRF token (constant-time comparison)
|
||||||
|
*/
|
||||||
|
public static function validateToken($token) {
|
||||||
|
if (!isset($_SESSION[self::$tokenName])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::isTokenExpired()) {
|
||||||
|
self::generateToken(); // Auto-regenerate expired token
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant-time comparison to prevent timing attacks
|
||||||
|
return hash_equals($_SESSION[self::$tokenName], $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if token is expired
|
||||||
|
*/
|
||||||
|
private static function isTokenExpired() {
|
||||||
|
return !isset($_SESSION[self::$tokenTime]) ||
|
||||||
|
(time() - $_SESSION[self::$tokenTime]) > self::$tokenLifetime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
11
migrations/013_add_performance_indexes.sql
Normal file
11
migrations/013_add_performance_indexes.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-- Migration 013: Add performance indexes for critical queries
|
||||||
|
|
||||||
|
-- Index on ticket_comments.ticket_id (foreign key without index)
|
||||||
|
-- Speeds up comment loading by 10-100x on large tables
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_ticket_comments_ticket_id
|
||||||
|
ON ticket_comments(ticket_id);
|
||||||
|
|
||||||
|
-- Composite index on audit_log for entity lookups with date sorting
|
||||||
|
-- Optimizes activity timeline queries
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_entity_created
|
||||||
|
ON audit_log(entity_type, entity_id, created_at DESC);
|
||||||
4
migrations/013_rollback.sql
Normal file
4
migrations/013_rollback.sql
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
-- Rollback for migration 013: Remove performance indexes
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS idx_ticket_comments_ticket_id ON ticket_comments;
|
||||||
|
DROP INDEX IF EXISTS idx_audit_entity_created ON audit_log;
|
||||||
Reference in New Issue
Block a user