Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
213
src/Framework/View/Processors/CsrfTokenProcessor.php
Normal file
213
src/Framework/View/Processors/CsrfTokenProcessor.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\View\Processors;
|
||||
|
||||
use App\Framework\Template\Processing\StringProcessor;
|
||||
use App\Framework\View\RenderContext;
|
||||
|
||||
/**
|
||||
* CSRF-Token-Processor für Cache-sichere CSRF-Token-Behandlung
|
||||
*
|
||||
* Dieser Processor löst das Problem mit gecachten CSRF-Tokens:
|
||||
* 1. Beim Caching: Ersetzt CSRF-Tokens mit Platzhaltern
|
||||
* 2. Beim Rendern: Ersetzt Platzhalter mit frischen Tokens
|
||||
*/
|
||||
final class CsrfTokenProcessor implements StringProcessor
|
||||
{
|
||||
private const string CSRF_PLACEHOLDER = '__CSRF_TOKEN_PLACEHOLDER__';
|
||||
private const string META_PLACEHOLDER = '__CSRF_META_PLACEHOLDER__';
|
||||
private const string FIELD_PLACEHOLDER = '__CSRF_FIELD_PLACEHOLDER__';
|
||||
|
||||
public function __construct(
|
||||
private readonly bool $cacheMode = false,
|
||||
private readonly bool $debugMode = false
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(string $html, RenderContext $context): string
|
||||
{
|
||||
return $html;
|
||||
|
||||
if (! $this->shouldProcessCsrf($context)) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
if ($this->cacheMode) {
|
||||
// Cache-Modus: Ersetze echte Tokens mit Platzhaltern
|
||||
return $this->replaceTokensWithPlaceholders($html, $context);
|
||||
} else {
|
||||
// Render-Modus: Ersetze Platzhalter mit echten Tokens
|
||||
return $this->replacePlaceholdersWithTokens($html, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob das Template CSRF-Token-Processing benötigt
|
||||
*/
|
||||
private function shouldProcessCsrf(RenderContext $context): bool
|
||||
{
|
||||
// Check 1: Data enthält CSRF-Token
|
||||
if (isset($context->data['csrf_token']) || isset($context->data['_token'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check 2: Template-Name deutet auf Formulare hin
|
||||
$formKeywords = ['form', 'contact', 'login', 'register', 'checkout', 'admin'];
|
||||
foreach ($formKeywords as $keyword) {
|
||||
if (str_contains($context->template, $keyword)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache-Modus: Ersetzt echte CSRF-Tokens mit Platzhaltern
|
||||
*/
|
||||
private function replaceTokensWithPlaceholders(string $html, RenderContext $context): string
|
||||
{
|
||||
$this->debugLog("Converting CSRF tokens to placeholders for caching");
|
||||
|
||||
// 1. Ersetze Template-Syntax CSRF-Aufrufe
|
||||
$html = $this->replaceTemplateCsrfCalls($html);
|
||||
|
||||
// 2. Ersetze echte Token-Werte falls vorhanden
|
||||
if (isset($context->data['csrf_token'])) {
|
||||
$html = str_replace($context->data['csrf_token'], self::CSRF_PLACEHOLDER, $html);
|
||||
}
|
||||
|
||||
if (isset($context->data['_token'])) {
|
||||
$html = str_replace($context->data['_token'], self::CSRF_PLACEHOLDER, $html);
|
||||
}
|
||||
|
||||
// 3. Ersetze HTML-Pattern
|
||||
$html = $this->replaceHtmlCsrfPatterns($html);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render-Modus: Ersetzt Platzhalter mit echten CSRF-Tokens
|
||||
*/
|
||||
private function replacePlaceholdersWithTokens(string $html, RenderContext $context): string
|
||||
{
|
||||
$this->debugLog("Converting placeholders to fresh CSRF tokens");
|
||||
|
||||
$csrfToken = $this->getCsrfToken($context);
|
||||
|
||||
// Ersetze alle Platzhalter-Types
|
||||
$replacements = [
|
||||
self::CSRF_PLACEHOLDER => $csrfToken,
|
||||
self::META_PLACEHOLDER => $csrfToken,
|
||||
self::FIELD_PLACEHOLDER => $this->generateCsrfField($csrfToken),
|
||||
];
|
||||
|
||||
return str_replace(array_keys($replacements), array_values($replacements), $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersetzt Template-Syntax CSRF-Aufrufe mit Platzhaltern
|
||||
*/
|
||||
private function replaceTemplateCsrfCalls(string $html): string
|
||||
{
|
||||
$patterns = [
|
||||
// {{ csrf_token() }} oder {{ csrf_token }}
|
||||
'/\{\{\s*csrf_token\(\s*\)\s*\}\}/' => self::CSRF_PLACEHOLDER,
|
||||
'/\{\{\s*csrf_token\s*\}\}/' => self::CSRF_PLACEHOLDER,
|
||||
|
||||
// {!! csrf_token() !!}
|
||||
'/\{!!\s*csrf_token\(\s*\)\s*!!\}/' => self::CSRF_PLACEHOLDER,
|
||||
|
||||
// {{ _token }}
|
||||
'/\{\{\s*_token\s*\}\}/' => self::CSRF_PLACEHOLDER,
|
||||
|
||||
// @csrf Blade-Direktive
|
||||
'/@csrf/' => self::FIELD_PLACEHOLDER,
|
||||
|
||||
// csrf_field() Helper
|
||||
'/\{\{\s*csrf_field\(\s*\)\s*\}\}/' => self::FIELD_PLACEHOLDER,
|
||||
'{!! csrf_field() !!}' => self::FIELD_PLACEHOLDER,
|
||||
];
|
||||
|
||||
return preg_replace(array_keys($patterns), array_values($patterns), $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersetzt HTML-CSRF-Patterns mit Platzhaltern
|
||||
*/
|
||||
private function replaceHtmlCsrfPatterns(string $html): string
|
||||
{
|
||||
// Hidden Input Fields mit CSRF-Token
|
||||
$html = preg_replace(
|
||||
'/<input[^>]*name=["\']_token["\'][^>]*value=["\']([^"\']+)["\'][^>]*>/',
|
||||
'<input type="hidden" name="_token" value="' . self::CSRF_PLACEHOLDER . '">',
|
||||
$html
|
||||
);
|
||||
|
||||
// Meta Tags mit CSRF-Token
|
||||
$html = preg_replace(
|
||||
'/<meta[^>]*name=["\']csrf-token["\'][^>]*content=["\']([^"\']+)["\'][^>]*>/',
|
||||
'<meta name="csrf-token" content="' . self::META_PLACEHOLDER . '">',
|
||||
$html
|
||||
);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt den aktuellen CSRF-Token
|
||||
*/
|
||||
private function getCsrfToken(RenderContext $context): string
|
||||
{
|
||||
// Priorität: Context-Daten > Generierter Token
|
||||
return $context->data['csrf_token'] ??
|
||||
$context->data['_token'] ??
|
||||
$this->generateFreshCsrfToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert einen neuen CSRF-Token
|
||||
*/
|
||||
private function generateFreshCsrfToken(): string
|
||||
{
|
||||
// Integration mit deinem CSRF-System
|
||||
// Das könnte auch ein Service-Call sein
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert ein komplettes CSRF-Hidden-Field
|
||||
*/
|
||||
private function generateCsrfField(string $token): string
|
||||
{
|
||||
return '<input type="hidden" name="_token" value="' . $token . '">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug-Logging
|
||||
*/
|
||||
private function debugLog(string $message): void
|
||||
{
|
||||
// Debug logging removed for production
|
||||
// Use proper logger in production environment
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory-Methode für Cache-Modus
|
||||
*/
|
||||
public static function forCaching(bool $debugMode = false): self
|
||||
{
|
||||
return new self(cacheMode: true, debugMode: $debugMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory-Methode für Render-Modus
|
||||
*/
|
||||
public static function forRendering(bool $debugMode = false): self
|
||||
{
|
||||
return new self(cacheMode: false, debugMode: $debugMode);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user