/i', // JavaScript execution vectors '/javascript\s*:/i', '/vbscript\s*:/i', '/data\s*:\s*text\/html/i', // Inline event handlers (strong signal) '/on\w+\s*=\s*["\']?/i', // Dangerous HTML tags '/<\s*iframe\b/i', '/<\s*object\b/i', '/<\s*embed\b/i', '/<\s*applet\b/i', '/<\s*img\b/i', // Image-based execution '/<\s*img\b[^>]*on\w+/i', // SVG-based execution (modern bypass) '/<\s*svg\b/i', '/<\s*math\b/i', // Meta refresh redirect '/<\s*meta\b[^>]*http-equiv\s*=\s*["\']?refresh/i', // HTML injection via src/href '/<\s*\w+\b[^>]*(src|href)\s*=\s*["\']?\s*(javascript|data)\s*:/i' ]; public function before(RequestInterface $request, $arguments = null) { $logger = Services::mylogger(); $response = Services::response(); // Collect all user-controlled input $inputs = array_merge( $request->getGet(), $request->getPost() ); if (empty($inputs)) { return; } foreach ($inputs as $field => $value) { if (is_array($value)) { $value = json_encode($value); } // Step 1: Canonicalization (VERY IMPORTANT) $canonical = $this->canonicalize($value); // Step 2: Trim (hygiene) $canonical = trim($canonical); // Step 3: Detection (signal-only) if ($this->detectXss($canonical)) { // 🔐 Log intent, not data $logger->logme('critical','SECURITY_BLOCKED_REQUEST - '. json_encode([ 'ip' => $request->getIPAddress(), 'method' => $request->getMethod(), 'uri' => current_url(), 'field' => $field, 'attack' => 'XSS_PATTERN', 'length' => strlen($canonical), 'hash' => hash('sha256', $canonical), ])); // ⛔ Block request return $response ->setStatusCode(403) ->setJSON([ 'status' => 403, 'error' => 'Forbidden', 'message' => 'Malicious input detected' ]); } } } public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // no-op } /** * Canonicalization prevents encoded bypass */ private function canonicalize(string $value): string { $value = urldecode($value); $value = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); // Remove invisible control characters return preg_replace('/[\x00-\x1F\x7F]/u', '', $value); } private function detectXss(string $value): bool { foreach ($this->xssPatterns as $pattern) { if (preg_match($pattern, $value)) { return true; } } return false; } }