- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
366 lines
12 KiB
PHP
366 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Exception\Security;
|
|
|
|
use App\Framework\Exception\Core\SecurityErrorCode;
|
|
use App\Framework\Exception\ExceptionContext;
|
|
use App\Framework\Exception\FrameworkException;
|
|
|
|
/**
|
|
* Ausnahme für XSS-Angriffs-Versuche
|
|
*
|
|
* Verwendet OWASP-konforme Nachrichten für XSS-Detection
|
|
*/
|
|
final class XssAttemptException extends FrameworkException
|
|
{
|
|
/**
|
|
* @param string $clientIp Client-IP für Security-Tracking
|
|
* @param string $field Betroffenes Feld/Parameter
|
|
* @param string $pattern Erkanntes XSS-Pattern
|
|
* @param string $originalValue Ursprünglicher Wert (sanitized)
|
|
* @param string $xssType Art des XSS (reflected, stored, dom)
|
|
* @param \Throwable|null $previous Vorherige Ausnahme
|
|
*/
|
|
public function __construct(
|
|
public readonly string $clientIp,
|
|
public readonly string $field,
|
|
public readonly string $pattern,
|
|
public readonly string $originalValue,
|
|
public readonly string $xssType = 'reflected',
|
|
?\Throwable $previous = null
|
|
) {
|
|
// OWASP-konforme Nachricht mit Platzhaltern
|
|
$message = "Client {$this->clientIp} attempted XSS attack in field {$this->field}";
|
|
|
|
$context = ExceptionContext::forOperation('security.xss_detection', 'Security')
|
|
->withData([
|
|
'client_ip' => $this->clientIp,
|
|
'field' => $this->field,
|
|
'pattern' => $this->pattern,
|
|
'xss_type' => $this->xssType,
|
|
'value_length' => strlen($this->originalValue),
|
|
'event_identifier' => "security_xss_attempt:{$this->clientIp},{$this->field}",
|
|
'category' => 'input_validation',
|
|
'requires_alert' => true, // XSS-Versuche erfordern immer Alerts
|
|
])
|
|
->withDebug([
|
|
'sanitized_value' => $this->sanitizeValueForLog($this->originalValue),
|
|
])
|
|
->withMetadata([
|
|
'security_event' => true,
|
|
'owasp_compliant' => true,
|
|
'log_level' => 'ERROR',
|
|
'attack_type' => 'xss',
|
|
'critical_security_event' => true,
|
|
]);
|
|
|
|
parent::__construct(
|
|
message: $message,
|
|
context: $context,
|
|
code: 400, // Bad Request
|
|
previous: $previous,
|
|
errorCode: SecurityErrorCode::XSS_DETECTED
|
|
);
|
|
}
|
|
|
|
// === Factory Methods für verschiedene XSS-Patterns ===
|
|
|
|
public static function scriptTag(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, '<script> tag injection', $value, 'reflected');
|
|
}
|
|
|
|
public static function onEventHandler(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'Event handler injection (onclick, onload, etc.)', $value, 'reflected');
|
|
}
|
|
|
|
public static function javascriptProtocol(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'javascript: protocol injection', $value, 'reflected');
|
|
}
|
|
|
|
public static function htmlInjection(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'HTML tag injection', $value, 'reflected');
|
|
}
|
|
|
|
public static function domBasedXss(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'DOM-based XSS pattern', $value, 'dom');
|
|
}
|
|
|
|
public static function storedXss(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'Stored XSS pattern', $value, 'stored');
|
|
}
|
|
|
|
public static function cssInjection(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'CSS injection with expression()', $value, 'reflected');
|
|
}
|
|
|
|
public static function svgXss(string $clientIp, string $field, string $value): self
|
|
{
|
|
return new self($clientIp, $field, 'SVG-based XSS injection', $value, 'reflected');
|
|
}
|
|
|
|
/**
|
|
* Gibt OWASP-konforme Event-Daten zurück
|
|
*/
|
|
public function getSecurityEventData(): array
|
|
{
|
|
return [
|
|
'event_identifier' => "security_xss_attempt:{$this->clientIp},{$this->field}",
|
|
'description' => "Client {$this->clientIp} attempted XSS attack in field {$this->field}",
|
|
'category' => 'input_validation',
|
|
'log_level' => 'ERROR',
|
|
'requires_alert' => true,
|
|
'client_ip' => $this->clientIp,
|
|
'field' => $this->field,
|
|
'pattern' => $this->pattern,
|
|
'xss_type' => $this->xssType,
|
|
'attack_severity' => $this->getAttackSeverity(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Bestimmt Schweregrad des XSS-Angriffs
|
|
*/
|
|
public function getAttackSeverity(): string
|
|
{
|
|
return match ($this->xssType) {
|
|
'stored' => 'CRITICAL', // Stored XSS ist am gefährlichsten
|
|
'dom' => 'HIGH', // DOM-based XSS ist schwer zu erkennen
|
|
'reflected' => 'MEDIUM', // Reflected XSS ist häufig aber weniger persistent
|
|
default => 'MEDIUM',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* XSS-Versuche erfordern immer Alerts
|
|
*/
|
|
public function requiresAlert(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Prüft ob es ein automatisierter Angriff ist
|
|
*/
|
|
public function isAutomatedAttack(): bool
|
|
{
|
|
$automatedPatterns = [
|
|
'alert(',
|
|
'prompt(',
|
|
'confirm(',
|
|
'document.cookie',
|
|
'xss',
|
|
'<svg',
|
|
'javascript:',
|
|
'onerror=',
|
|
'onload=',
|
|
];
|
|
|
|
$lowerValue = strtolower($this->originalValue);
|
|
foreach ($automatedPatterns as $pattern) {
|
|
if (str_contains($lowerValue, $pattern)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Analysiert das XSS-Pattern detailliert
|
|
*/
|
|
public function analyzePattern(): array
|
|
{
|
|
$analysis = [
|
|
'vector_type' => 'unknown',
|
|
'payload_complexity' => 'low',
|
|
'encoding_used' => false,
|
|
'obfuscation_detected' => false,
|
|
'automation_detected' => $this->isAutomatedAttack(),
|
|
'potential_impact' => 'low',
|
|
];
|
|
|
|
$lowerValue = strtolower($this->originalValue);
|
|
|
|
// Vector-Typ-Erkennung
|
|
if (str_contains($lowerValue, '<script')) {
|
|
$analysis['vector_type'] = 'script_tag';
|
|
} elseif (str_contains($lowerValue, 'on') && preg_match('/on\w+\s*=/', $lowerValue)) {
|
|
$analysis['vector_type'] = 'event_handler';
|
|
} elseif (str_contains($lowerValue, 'javascript:')) {
|
|
$analysis['vector_type'] = 'javascript_protocol';
|
|
} elseif (str_contains($lowerValue, '<svg') || str_contains($lowerValue, '<embed')) {
|
|
$analysis['vector_type'] = 'svg_embed';
|
|
} elseif (str_contains($lowerValue, 'expression(')) {
|
|
$analysis['vector_type'] = 'css_expression';
|
|
}
|
|
|
|
// Komplexität bewerten
|
|
if (strlen($this->originalValue) > 100) {
|
|
$analysis['payload_complexity'] = 'high';
|
|
} elseif (strlen($this->originalValue) > 50) {
|
|
$analysis['payload_complexity'] = 'medium';
|
|
}
|
|
|
|
// Encoding-Erkennung
|
|
if (str_contains($this->originalValue, '%') || str_contains($this->originalValue, '&#')) {
|
|
$analysis['encoding_used'] = true;
|
|
}
|
|
|
|
// Obfuskierung-Erkennung
|
|
if (preg_match('/String\.fromCharCode|eval\(|unescape\(/', $this->originalValue)) {
|
|
$analysis['obfuscation_detected'] = true;
|
|
$analysis['payload_complexity'] = 'high';
|
|
}
|
|
|
|
// Impact-Bewertung
|
|
if (str_contains($lowerValue, 'cookie') || str_contains($lowerValue, 'document')) {
|
|
$analysis['potential_impact'] = 'high';
|
|
} elseif (str_contains($lowerValue, 'alert') || str_contains($lowerValue, 'prompt')) {
|
|
$analysis['potential_impact'] = 'medium';
|
|
}
|
|
|
|
return $analysis;
|
|
}
|
|
|
|
/**
|
|
* Gibt benutzerfreundliche Fehlermeldung zurück (ohne Details zu verraten)
|
|
*/
|
|
public function getUserMessage(): string
|
|
{
|
|
return "Invalid input detected. Please check your data and try again.";
|
|
}
|
|
|
|
/**
|
|
* Bereinigt Wert für sicheres Logging
|
|
*/
|
|
private function sanitizeValueForLog(string $value): string
|
|
{
|
|
// HTML-Entities kodieren und auf 100 Zeichen begrenzen
|
|
$sanitized = htmlspecialchars(substr($value, 0, 100), ENT_QUOTES, 'UTF-8');
|
|
|
|
// Zusätzliche Bereinigung für Logs
|
|
$sanitized = preg_replace('/\s+/', ' ', $sanitized);
|
|
|
|
return trim($sanitized);
|
|
}
|
|
|
|
/**
|
|
* Gibt spezifische Verteidigungsempfehlung zurück
|
|
*/
|
|
public function getDefenseRecommendation(): string
|
|
{
|
|
return match ($this->xssType) {
|
|
'stored' => 'Implement output encoding, Content Security Policy, and input validation.',
|
|
'dom' => 'Use safe JavaScript APIs, validate DOM manipulation, implement CSP.',
|
|
'reflected' => 'Implement output encoding, input validation, and Content Security Policy.',
|
|
default => 'Use output encoding, input validation, and Content Security Policy.',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generiert Content Security Policy Empfehlungen
|
|
*/
|
|
public function getCspRecommendations(): array
|
|
{
|
|
$baseCSP = [
|
|
"default-src 'self'",
|
|
"script-src 'self'",
|
|
"style-src 'self' 'unsafe-inline'",
|
|
"img-src 'self' data:",
|
|
"connect-src 'self'",
|
|
"font-src 'self'",
|
|
"object-src 'none'",
|
|
"frame-src 'none'",
|
|
];
|
|
|
|
$analysis = $this->analyzePattern();
|
|
|
|
// Verschärfungen basierend auf Angriffsmuster
|
|
if ($analysis['vector_type'] === 'script_tag') {
|
|
$baseCSP[] = "script-src 'self' 'nonce-{random}'"; // Nonce-basierte Scripts
|
|
}
|
|
|
|
if ($analysis['vector_type'] === 'css_expression') {
|
|
$baseCSP[] = "style-src 'self'"; // Entferne unsafe-inline
|
|
}
|
|
|
|
return $baseCSP;
|
|
}
|
|
|
|
/**
|
|
* Generiert IOC (Indicator of Compromise) für Security-Teams
|
|
*/
|
|
public function generateIOC(): array
|
|
{
|
|
return [
|
|
'type' => 'xss_attempt',
|
|
'source_ip' => $this->clientIp,
|
|
'target_field' => $this->field,
|
|
'pattern' => $this->pattern,
|
|
'xss_type' => $this->xssType,
|
|
'severity' => $this->getAttackSeverity(),
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
|
'automated' => $this->isAutomatedAttack(),
|
|
'analysis' => $this->analyzePattern(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Prüft ob sofortige Gegenmaßnahmen erforderlich sind
|
|
*/
|
|
public function requiresImmediateAction(): bool
|
|
{
|
|
return $this->xssType === 'stored' ||
|
|
$this->getAttackSeverity() === 'CRITICAL' ||
|
|
$this->isAutomatedAttack();
|
|
}
|
|
|
|
/**
|
|
* Gibt WAF (Web Application Firewall) Regel-Vorschläge zurück
|
|
*/
|
|
public function getWafRuleSuggestions(): array
|
|
{
|
|
$rules = [];
|
|
|
|
$analysis = $this->analyzePattern();
|
|
|
|
switch ($analysis['vector_type']) {
|
|
case 'script_tag':
|
|
$rules[] = 'Block requests containing <script tags in input fields';
|
|
|
|
break;
|
|
case 'event_handler':
|
|
$rules[] = 'Block requests containing on* event handlers in input fields';
|
|
|
|
break;
|
|
case 'javascript_protocol':
|
|
$rules[] = 'Block requests containing javascript: protocol in input fields';
|
|
|
|
break;
|
|
case 'svg_embed':
|
|
$rules[] = 'Block or sanitize SVG/embed tags in user input';
|
|
|
|
break;
|
|
}
|
|
|
|
if ($analysis['encoding_used']) {
|
|
$rules[] = 'Implement URL decoding before XSS detection';
|
|
}
|
|
|
|
if ($analysis['obfuscation_detected']) {
|
|
$rules[] = 'Implement advanced obfuscation detection rules';
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
}
|