- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
347 lines
14 KiB
PHP
347 lines
14 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Application\Security;
|
|
|
|
use App\Application\Security\Events\Auth\{
|
|
AuthenticationFailedEvent
|
|
};
|
|
use App\Application\Security\Events\Input\{
|
|
InputValidationFailureEvent,
|
|
MaliciousInputDetectedEvent,
|
|
SqlInjectionAttemptEvent,
|
|
XssAttemptEvent
|
|
};
|
|
use App\Application\Security\Events\Network\SuspiciousNetworkActivityEvent;
|
|
use App\Application\Security\Events\System\SystemAnomalyEvent;
|
|
use App\Application\Security\ValueObjects\{
|
|
OWASPEventIdentifier,
|
|
OWASPLogLevel,
|
|
RequestContext,
|
|
SecurityContext
|
|
};
|
|
use App\Framework\Waf\DetectionCategory;
|
|
use App\Framework\Waf\DetectionSeverity;
|
|
use App\Framework\Waf\ValueObjects\Detection;
|
|
|
|
/**
|
|
* WAF-OWASP Event Bridge
|
|
*
|
|
* Consolidates the WAF Detection system with the existing OWASP Security Event framework
|
|
* to eliminate duplication and provide unified security event handling.
|
|
*/
|
|
final class WafOWASPEventBridge
|
|
{
|
|
public function __construct(
|
|
private readonly OWASPSecurityEventFactory $eventFactory,
|
|
private readonly OWASPSecurityEventLogger $eventLogger
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Convert WAF Detection to OWASP Security Event and log it
|
|
*/
|
|
public function processWafDetection(
|
|
Detection $detection,
|
|
SecurityContext $securityContext,
|
|
RequestContext $requestContext
|
|
): OWASPSecurityEvent {
|
|
$owaspEvent = $this->convertDetectionToOWASPEvent($detection, $requestContext);
|
|
|
|
// Log using existing OWASP logger
|
|
$logFormat = $this->eventFactory->createFromDetection(
|
|
$detection,
|
|
$owaspEvent,
|
|
$securityContext,
|
|
$requestContext
|
|
);
|
|
|
|
$this->eventLogger->log($logFormat);
|
|
|
|
return $owaspEvent;
|
|
}
|
|
|
|
/**
|
|
* Convert WAF Detection to appropriate OWASP Security Event
|
|
*/
|
|
public function convertDetectionToOWASPEvent(
|
|
Detection $detection,
|
|
RequestContext $requestContext
|
|
): OWASPSecurityEvent {
|
|
return match ($detection->category) {
|
|
DetectionCategory::SQL_INJECTION => $this->createSqlInjectionEvent($detection, $requestContext),
|
|
DetectionCategory::XSS => $this->createXssEvent($detection, $requestContext),
|
|
DetectionCategory::INJECTION,
|
|
DetectionCategory::COMMAND_INJECTION,
|
|
DetectionCategory::LDAP_INJECTION,
|
|
DetectionCategory::XPATH_INJECTION,
|
|
DetectionCategory::NOSQL_INJECTION => $this->createMaliciousInputEvent($detection, $requestContext),
|
|
DetectionCategory::BRUTE_FORCE,
|
|
DetectionCategory::CREDENTIAL_STUFFING,
|
|
DetectionCategory::AUTHENTICATION_BYPASS => $this->createAuthenticationFailedEvent($detection, $requestContext),
|
|
DetectionCategory::SUSPICIOUS_IP,
|
|
DetectionCategory::MALICIOUS_BOT,
|
|
DetectionCategory::DOS_ATTACK,
|
|
DetectionCategory::DDOS_ATTACK => $this->createSuspiciousNetworkActivityEvent($detection, $requestContext),
|
|
DetectionCategory::RATE_LIMIT_VIOLATION,
|
|
DetectionCategory::ANOMALOUS_BEHAVIOR,
|
|
DetectionCategory::SUSPICIOUS_USER_AGENT => $this->createSystemAnomalyEvent($detection, $requestContext),
|
|
default => $this->createGenericInputValidationEvent($detection, $requestContext)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create SQL Injection OWASP event from WAF detection
|
|
*/
|
|
private function createSqlInjectionEvent(Detection $detection, RequestContext $requestContext): SqlInjectionAttemptEvent
|
|
{
|
|
$payload = $detection->payload?->getSample() ?? $detection->message;
|
|
$targetField = $detection->location ?? 'unknown_field';
|
|
$detectionMethod = "WAF Rule {($detection->ruleId?->value) ?? 'generic'}";
|
|
|
|
return new SqlInjectionAttemptEvent(
|
|
attackPayload: $payload,
|
|
targetField: $targetField,
|
|
detectionMethod: $detectionMethod,
|
|
email: $requestContext->getUserEmail()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create XSS OWASP event from WAF detection
|
|
*/
|
|
private function createXssEvent(Detection $detection, RequestContext $requestContext): XssAttemptEvent
|
|
{
|
|
$payload = $detection->payload?->getSample() ?? $detection->message;
|
|
$targetField = $detection->location ?? 'unknown_field';
|
|
$xssType = $this->determineXssType($payload);
|
|
|
|
return new XssAttemptEvent(
|
|
attackPayload: $payload,
|
|
targetField: $targetField,
|
|
xssType: $xssType,
|
|
email: $requestContext->getUserEmail()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create Malicious Input OWASP event from WAF detection
|
|
*/
|
|
private function createMaliciousInputEvent(Detection $detection, RequestContext $requestContext): MaliciousInputDetectedEvent
|
|
{
|
|
$payload = $detection->payload?->getSample() ?? $detection->message;
|
|
$inputType = $this->mapDetectionCategoryToInputType($detection->category);
|
|
|
|
return new MaliciousInputDetectedEvent(
|
|
inputPayload: $payload,
|
|
inputType: $inputType,
|
|
detectionMethod: "WAF Rule {($detection->ruleId?->value) ?? 'generic'}",
|
|
email: $requestContext->getUserEmail()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create Authentication Failed OWASP event from WAF detection
|
|
*/
|
|
private function createAuthenticationFailedEvent(Detection $detection, RequestContext $requestContext): AuthenticationFailedEvent
|
|
{
|
|
$attackType = $this->mapDetectionCategoryToAttackType($detection->category);
|
|
$email = $requestContext->getUserEmail() ?? 'anonymous@waf.detection';
|
|
|
|
return new AuthenticationFailedEvent(
|
|
email: $email,
|
|
reason: $attackType,
|
|
failedAttempts: 1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create Suspicious Network Activity OWASP event from WAF detection
|
|
*/
|
|
private function createSuspiciousNetworkActivityEvent(Detection $detection, RequestContext $requestContext): SuspiciousNetworkActivityEvent
|
|
{
|
|
$activityType = $this->mapDetectionCategoryToActivityType($detection->category);
|
|
|
|
return new SuspiciousNetworkActivityEvent(
|
|
sourceIp: $requestContext->getClientIp(),
|
|
activityType: $activityType,
|
|
requestCount: 1, // WAF detections are typically single requests
|
|
timeWindow: '1 minute',
|
|
email: $requestContext->getUserEmail()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create System Anomaly OWASP event from WAF detection
|
|
*/
|
|
private function createSystemAnomalyEvent(Detection $detection, RequestContext $requestContext): SystemAnomalyEvent
|
|
{
|
|
$anomalyType = $this->mapDetectionCategoryToAnomalyType($detection->category);
|
|
$metrics = [
|
|
'threat_score' => $detection->getThreatScore()->getValue(),
|
|
'confidence' => $detection->confidence?->getValue(),
|
|
'location' => $detection->location,
|
|
'client_ip' => $requestContext->getClientIp(),
|
|
'rule_id' => $detection->ruleId?->value,
|
|
];
|
|
|
|
return new SystemAnomalyEvent(
|
|
anomalyType: $anomalyType,
|
|
description: $detection->message,
|
|
metrics: array_filter($metrics),
|
|
severity: $this->mapSeverityToString($detection->severity)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create generic Input Validation OWASP event from WAF detection
|
|
*/
|
|
private function createGenericInputValidationEvent(Detection $detection, RequestContext $requestContext): InputValidationFailureEvent
|
|
{
|
|
$field = $detection->location ?? 'unknown_field';
|
|
$reason = $detection->category->getDescription();
|
|
|
|
return new InputValidationFailureEvent(
|
|
field: $field,
|
|
reason: $reason,
|
|
attemptedValue: $detection->payload?->getSample() ?? '',
|
|
email: $requestContext->getUserEmail()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Category to Input Type
|
|
*/
|
|
private function mapDetectionCategoryToInputType(DetectionCategory $category): string
|
|
{
|
|
return match ($category) {
|
|
DetectionCategory::COMMAND_INJECTION => 'command_injection',
|
|
DetectionCategory::LDAP_INJECTION => 'ldap_injection',
|
|
DetectionCategory::XPATH_INJECTION => 'xpath_injection',
|
|
DetectionCategory::NOSQL_INJECTION => 'nosql_injection',
|
|
DetectionCategory::XXE => 'xml_external_entity',
|
|
DetectionCategory::PATH_TRAVERSAL => 'path_traversal',
|
|
DetectionCategory::DESERIALIZATION => 'unsafe_deserialization',
|
|
default => 'generic_injection'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Category to Attack Type
|
|
*/
|
|
private function mapDetectionCategoryToAttackType(DetectionCategory $category): string
|
|
{
|
|
return match ($category) {
|
|
DetectionCategory::BRUTE_FORCE => 'brute_force',
|
|
DetectionCategory::CREDENTIAL_STUFFING => 'credential_stuffing',
|
|
DetectionCategory::AUTHENTICATION_BYPASS => 'authentication_bypass',
|
|
DetectionCategory::SESSION_FIXATION => 'session_fixation',
|
|
DetectionCategory::SESSION_HIJACKING => 'session_hijacking',
|
|
default => 'authentication_attack'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Category to Activity Type
|
|
*/
|
|
private function mapDetectionCategoryToActivityType(DetectionCategory $category): string
|
|
{
|
|
return match ($category) {
|
|
DetectionCategory::SUSPICIOUS_IP => 'suspicious_ip_activity',
|
|
DetectionCategory::MALICIOUS_BOT => 'malicious_bot_activity',
|
|
DetectionCategory::DOS_ATTACK => 'denial_of_service',
|
|
DetectionCategory::DDOS_ATTACK => 'distributed_denial_of_service',
|
|
DetectionCategory::SCRAPING_BOT => 'web_scraping',
|
|
DetectionCategory::SPAM_BOT => 'spam_activity',
|
|
DetectionCategory::AUTOMATED_ATTACK => 'automated_attack',
|
|
DetectionCategory::TOR_EXIT_NODE => 'tor_network_activity',
|
|
DetectionCategory::PROXY_DETECTION => 'proxy_usage',
|
|
default => 'suspicious_network_activity'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Category to Anomaly Type
|
|
*/
|
|
private function mapDetectionCategoryToAnomalyType(DetectionCategory $category): string
|
|
{
|
|
return match ($category) {
|
|
DetectionCategory::RATE_LIMIT_VIOLATION => 'rate_limit_anomaly',
|
|
DetectionCategory::ANOMALOUS_BEHAVIOR => 'behavioral_anomaly',
|
|
DetectionCategory::SUSPICIOUS_USER_AGENT => 'user_agent_anomaly',
|
|
DetectionCategory::FINGERPRINTING_ATTEMPT => 'fingerprinting_anomaly',
|
|
DetectionCategory::RECONNAISSANCE => 'reconnaissance_anomaly',
|
|
DetectionCategory::POLICY_VIOLATION => 'policy_violation_anomaly',
|
|
default => 'system_anomaly'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Severity to String
|
|
*/
|
|
private function mapSeverityToString(DetectionSeverity $severity): string
|
|
{
|
|
return match ($severity) {
|
|
DetectionSeverity::CRITICAL => 'critical',
|
|
DetectionSeverity::HIGH => 'high',
|
|
DetectionSeverity::MEDIUM => 'medium',
|
|
DetectionSeverity::LOW => 'low',
|
|
DetectionSeverity::INFO => 'info'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create OWASP Event Identifier from WAF Detection
|
|
*/
|
|
public function createOWASPEventIdentifier(Detection $detection): OWASPEventIdentifier
|
|
{
|
|
return match ($detection->category) {
|
|
DetectionCategory::SQL_INJECTION => OWASPEventIdentifier::maliciousInput('sql_injection'),
|
|
DetectionCategory::XSS => OWASPEventIdentifier::maliciousInput('xss'),
|
|
DetectionCategory::COMMAND_INJECTION => OWASPEventIdentifier::maliciousInput('command_injection'),
|
|
DetectionCategory::AUTHENTICATION_BYPASS,
|
|
DetectionCategory::BRUTE_FORCE => OWASPEventIdentifier::authenticationFailure('anonymous'),
|
|
DetectionCategory::SESSION_HIJACKING => OWASPEventIdentifier::sessionHijacking('anonymous'),
|
|
DetectionCategory::PRIVILEGE_ESCALATION => OWASPEventIdentifier::privilegeEscalation('anonymous', 'user', 'admin'),
|
|
DetectionCategory::MALICIOUS_FILE_UPLOAD => OWASPEventIdentifier::fileUploadFailure('suspicious_file'),
|
|
DetectionCategory::DOS_ATTACK,
|
|
DetectionCategory::DDOS_ATTACK => OWASPEventIdentifier::systemAnomaly('denial_of_service'),
|
|
default => OWASPEventIdentifier::systemAnomaly($detection->category->value)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Map WAF Detection Severity to OWASP Log Level
|
|
*/
|
|
public function mapToOWASPLogLevel(Detection $detection): OWASPLogLevel
|
|
{
|
|
return match ($detection->severity) {
|
|
DetectionSeverity::CRITICAL => OWASPLogLevel::FATAL,
|
|
DetectionSeverity::HIGH => OWASPLogLevel::ERROR,
|
|
DetectionSeverity::MEDIUM => OWASPLogLevel::WARN,
|
|
DetectionSeverity::LOW => OWASPLogLevel::INFO,
|
|
DetectionSeverity::INFO => OWASPLogLevel::DEBUG
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Determine XSS type from payload
|
|
*/
|
|
private function determineXssType(string $payload): string
|
|
{
|
|
$payload = strtolower($payload);
|
|
|
|
if (str_contains($payload, '<script') || str_contains($payload, 'javascript:')) {
|
|
return 'stored_xss';
|
|
} elseif (str_contains($payload, 'onload=') || str_contains($payload, 'onerror=') ||
|
|
str_contains($payload, 'onclick=') || str_contains($payload, 'onmouse')) {
|
|
return 'reflected_xss';
|
|
} elseif (str_contains($payload, 'document.') || str_contains($payload, 'window.')) {
|
|
return 'dom_xss';
|
|
}
|
|
|
|
return 'generic_xss';
|
|
}
|
|
}
|