Files
michaelschiemer/src/Framework/Waf/Rules/OWASPCoreRuleSet.php
Michael Schiemer 55a330b223 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
2025-08-11 20:13:26 +02:00

474 lines
18 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Waf\Rules;
use App\Framework\Waf\DetectionCategory;
use App\Framework\Waf\DetectionSeverity;
use App\Framework\Waf\Rules\ValueObjects\RuleCondition;
use App\Framework\Waf\Rules\ValueObjects\RulePattern;
use App\Framework\Waf\ValueObjects\RuleId;
/**
* OWASP ModSecurity Core Rule Set (CRS) implementation
* Based on OWASP CRS v3.3+ patterns and rules
*/
final class OWASPCoreRuleSet
{
/**
* Get all OWASP CRS rules
*/
public static function getAllRules(): array
{
return array_merge(
self::getSqlInjectionRules(),
self::getXssRules(),
self::getPathTraversalRules(),
self::getCommandInjectionRules(),
self::getFileUploadRules(),
self::getUserAgentRules(),
self::getHttpProtocolRules(),
self::getApplicationAttackRules(),
self::getGenericAttackRules()
);
}
/**
* SQL Injection Detection Rules (OWASP Top 10 #3)
*/
public static function getSqlInjectionRules(): array
{
return [
// Basic SQL Injection patterns
new Rule(
id: RuleId::sql('920100'),
name: 'SQL Injection - Union Attack',
description: 'Detects SQL UNION attack patterns',
category: DetectionCategory::SQL_INJECTION,
severity: DetectionSeverity::CRITICAL,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:union[\s\/\*]+(?:all[\s\/\*]+)?select)', 'i')
),
action: RuleAction::BLOCK,
priority: 95,
tags: ['sql', 'injection', 'union', 'owasp-top10']
),
// SQL meta-characters
new Rule(
id: RuleId::sql('920110'),
name: 'SQL Injection - Meta Characters',
description: 'Detects SQL meta-characters in parameters',
category: DetectionCategory::SQL_INJECTION,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:[\'\"`][\s]*(?:or|and)[\s]*[\'\"`]*[\s]*(?:[\'\"`]*[\w]+[\'\"`]*[\s]*=[\s]*[\'\"`]*[\w]+|[\d]+[\s]*=[\s]*[\d]+))', 'i')
),
action: RuleAction::BLOCK,
priority: 90,
tags: ['sql', 'injection', 'meta-characters', 'owasp-top10']
),
// SQL comment attacks
new Rule(
id: RuleId::sql('920120'),
name: 'SQL Injection - Comment Attack',
description: 'Detects SQL comment-based injection',
category: DetectionCategory::SQL_INJECTION,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:--|#|\/\*|\*\/))', 'i')
),
action: RuleAction::SCORE,
priority: 80,
tags: ['sql', 'injection', 'comments']
),
// SQL functions
new Rule(
id: RuleId::sql('920130'),
name: 'SQL Injection - Function Detection',
description: 'Detects SQL functions in input',
category: DetectionCategory::SQL_INJECTION,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:concat|substring|ascii|char|count|group_concat|version|database|user|current_user|system_user|schema|table_name|column_name)[\s]*\()', 'i')
),
action: RuleAction::BLOCK,
priority: 85,
tags: ['sql', 'injection', 'functions', 'owasp-top10']
),
];
}
/**
* Cross-Site Scripting (XSS) Rules (OWASP Top 10 #7)
*/
public static function getXssRules(): array
{
return [
// Script tag injection
new Rule(
id: RuleId::xss('941100'),
name: 'XSS - Script Tag Attack',
description: 'Detects script tag injection attempts',
category: DetectionCategory::XSS,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:<script[^>]*>.*?<\/script>)', 'i')
),
action: RuleAction::BLOCK,
priority: 90,
tags: ['xss', 'script', 'owasp-top10']
),
// Event handler injection
new Rule(
id: RuleId::xss('941110'),
name: 'XSS - Event Handler Attack',
description: 'Detects JavaScript event handler injection',
category: DetectionCategory::XSS,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:on(?:load|error|click|focus|blur|change|submit|reset|select|keydown|keyup|keypress|mouseover|mouseout|mousedown|mouseup|mousemove)[\s]*=)', 'i')
),
action: RuleAction::BLOCK,
priority: 85,
tags: ['xss', 'events', 'owasp-top10']
),
// JavaScript pseudo protocol
new Rule(
id: RuleId::xss('941120'),
name: 'XSS - JavaScript Pseudo Protocol',
description: 'Detects javascript: pseudo protocol usage',
category: DetectionCategory::XSS,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:javascript[\s]*:)', 'i')
),
action: RuleAction::BLOCK,
priority: 80,
tags: ['xss', 'javascript', 'protocol']
),
// HTML injection
new Rule(
id: RuleId::xss('941130'),
name: 'XSS - HTML Tag Injection',
description: 'Detects potentially dangerous HTML tags',
category: DetectionCategory::XSS,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:<(?:iframe|object|embed|applet|meta|link|style|img|svg|math|details|template)[^>]*>)', 'i')
),
action: RuleAction::SCORE,
priority: 70,
tags: ['xss', 'html', 'tags']
),
];
}
/**
* Path Traversal Rules
*/
public static function getPathTraversalRules(): array
{
return [
// Directory traversal
new Rule(
id: RuleId::pathTraversal('930100'),
name: 'Path Traversal - Directory Traversal',
description: 'Detects directory traversal attempts',
category: DetectionCategory::PATH_TRAVERSAL,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::urlPath(
RulePattern::regex('(?i:(?:\.\.[\\/])|(?:[\\/]\.\.)|(?:\.\.\\\\)|(?:\\\\\.\.)|(?:%2e%2e%2f)|(?:%2e%2e\\\\)|(?:\.\.%2f)|(?:\.\.%5c)|(?:%2e%2e%5c)|(?:%c0%ae%c0%ae%c0%af)|(?:%c1%9c%c1%9c%c1%af))', 'i')
),
action: RuleAction::BLOCK,
priority: 85,
tags: ['path-traversal', 'directory', 'file-access']
),
// Absolute path access
new Rule(
id: RuleId::pathTraversal('930110'),
name: 'Path Traversal - Absolute Path Access',
description: 'Detects absolute path access attempts',
category: DetectionCategory::PATH_TRAVERSAL,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:\/etc\/passwd|\/etc\/shadow|\/etc\/hosts|\/proc\/|\/sys\/|c:[\\\\\\/]|\\\\\\\\))', 'i')
),
action: RuleAction::SCORE,
priority: 75,
tags: ['path-traversal', 'absolute-path', 'system-files']
),
];
}
/**
* Command Injection Rules
*/
public static function getCommandInjectionRules(): array
{
return [
// OS Command injection
new Rule(
id: RuleId::commandInjection('932100'),
name: 'Command Injection - OS Commands',
description: 'Detects OS command injection attempts',
category: DetectionCategory::COMMAND_INJECTION,
severity: DetectionSeverity::CRITICAL,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:;|\||\|\||&&|&|`|\$\(|\${)[\s]*(?:cat|ls|pwd|id|uname|whoami|ps|kill|rm|mv|cp|chmod|chown|find|grep|awk|sed|sort|head|tail|wc|netstat|ifconfig|ping|wget|curl|nc|telnet|ssh|su|sudo|passwd|shadow|etc\/passwd|etc\/shadow|proc\/))', 'i')
),
action: RuleAction::BLOCK,
priority: 95,
tags: ['command-injection', 'os-injection', 'rce', 'owasp-top10']
),
// Shell metacharacters
new Rule(
id: RuleId::commandInjection('932110'),
name: 'Command Injection - Shell Metacharacters',
description: 'Detects shell metacharacters',
category: DetectionCategory::COMMAND_INJECTION,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:[;&|`$(){}[\]<>])', 'i')
),
action: RuleAction::SCORE,
priority: 80,
tags: ['command-injection', 'shell', 'metacharacters']
),
];
}
/**
* File Upload Rules
*/
public static function getFileUploadRules(): array
{
return [
// Malicious file extensions
new Rule(
id: RuleId::generic('933100'),
name: 'File Upload - Malicious Extensions',
description: 'Detects potentially malicious file extensions',
category: DetectionCategory::FILE_UPLOAD_ABUSE,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::header(
'Content-Type',
RulePattern::regex('(?i:application\/(?:x-)?(?:php|jsp|asp|exe|bat|cmd|sh|python|perl|ruby|javascript)|text\/(?:x-)?(?:php|jsp|asp|python|perl|ruby))', 'i')
),
action: RuleAction::BLOCK,
priority: 85,
tags: ['file-upload', 'malware', 'webshell']
),
// Double extensions
new Rule(
id: RuleId::generic('933110'),
name: 'File Upload - Double Extensions',
description: 'Detects double file extensions',
category: DetectionCategory::FILE_UPLOAD_ABUSE,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:\.(?:jpg|jpeg|png|gif|bmp|doc|docx|pdf|txt)\.(?:php|jsp|asp|exe|bat|cmd|sh|py|pl|rb|js))', 'i')
),
action: RuleAction::SCORE,
priority: 70,
tags: ['file-upload', 'double-extension', 'bypass']
),
];
}
/**
* User Agent Rules
*/
public static function getUserAgentRules(): array
{
return [
// Security scanners
new Rule(
id: RuleId::generic('913100'),
name: 'User-Agent - Security Scanner Detection',
description: 'Detects known security scanning tools',
category: DetectionCategory::BOT_DETECTION,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::userAgent(
RulePattern::regex('(?i:(?:sqlmap|nmap|nikto|w3af|acunetix|nessus|openvas|burp|havij|hydra|metasploit|python-requests|curl|wget))', 'i')
),
action: RuleAction::CHALLENGE,
priority: 70,
tags: ['user-agent', 'scanner', 'tool-detection']
),
// Empty or missing User-Agent
new Rule(
id: RuleId::generic('913110'),
name: 'User-Agent - Missing or Empty',
description: 'Detects missing or empty user agent strings',
category: DetectionCategory::BOT_DETECTION,
severity: DetectionSeverity::LOW,
condition: RuleCondition::userAgent(
RulePattern::regex('^$', '')
),
action: RuleAction::SCORE,
priority: 40,
tags: ['user-agent', 'empty', 'suspicious']
),
];
}
/**
* HTTP Protocol Rules
*/
public static function getHttpProtocolRules(): array
{
return [
// Invalid HTTP methods
new Rule(
id: RuleId::generic('911100'),
name: 'HTTP Protocol - Invalid Method',
description: 'Detects invalid HTTP methods',
category: DetectionCategory::PROTOCOL_ATTACK,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::httpMethod(
RulePattern::regex('^(?!GET|POST|HEAD|PUT|DELETE|OPTIONS|PATCH|TRACE|CONNECT).*', 'i')
),
action: RuleAction::BLOCK,
priority: 60,
tags: ['http', 'protocol', 'method', 'invalid']
),
// Oversized request headers
new Rule(
id: RuleId::generic('911110'),
name: 'HTTP Protocol - Oversized Headers',
description: 'Detects oversized HTTP headers',
category: DetectionCategory::PROTOCOL_ATTACK,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::header(
'*',
RulePattern::regex('.{8192,}', '') // Headers over 8KB
),
action: RuleAction::BLOCK,
priority: 65,
tags: ['http', 'protocol', 'headers', 'size']
),
];
}
/**
* Application Attack Rules
*/
public static function getApplicationAttackRules(): array
{
return [
// LDAP injection
new Rule(
id: RuleId::generic('950100'),
name: 'Application Attack - LDAP Injection',
description: 'Detects LDAP injection attempts',
category: DetectionCategory::INJECTION_ATTACK,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:\()(?:&|\|)(?:\(|\)))', 'i')
),
action: RuleAction::BLOCK,
priority: 80,
tags: ['ldap', 'injection', 'application']
),
// XPath injection
new Rule(
id: RuleId::generic('950110'),
name: 'Application Attack - XPath Injection',
description: 'Detects XPath injection attempts',
category: DetectionCategory::INJECTION_ATTACK,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:(?:\/\/|\.\.|\[@|position\(\)|text\(\)|node\(\)|ancestor|descendant|following|preceding|self))', 'i')
),
action: RuleAction::BLOCK,
priority: 75,
tags: ['xpath', 'injection', 'xml']
),
];
}
/**
* Generic Attack Rules
*/
public static function getGenericAttackRules(): array
{
return [
// Null byte injection
new Rule(
id: RuleId::generic('960100'),
name: 'Generic Attack - Null Byte Injection',
description: 'Detects null byte injection attempts',
category: DetectionCategory::PROTOCOL_ATTACK,
severity: DetectionSeverity::HIGH,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:%00|\\x00|\0)', 'i')
),
action: RuleAction::BLOCK,
priority: 80,
tags: ['null-byte', 'injection', 'bypass']
),
// Unicode evasion
new Rule(
id: RuleId::generic('960110'),
name: 'Generic Attack - Unicode Evasion',
description: 'Detects Unicode-based evasion attempts',
category: DetectionCategory::EVASION,
severity: DetectionSeverity::MEDIUM,
condition: RuleCondition::requestBody(
RulePattern::regex('(?i:%u[0-9a-f]{4}|\\\\u[0-9a-f]{4})', 'i')
),
action: RuleAction::SCORE,
priority: 60,
tags: ['unicode', 'evasion', 'encoding']
),
];
}
/**
* Get rules by OWASP Top 10 category
*/
public static function getOwaspTop10Rules(): array
{
return array_filter(
self::getAllRules(),
fn (Rule $rule) => $rule->hasTag('owasp-top10')
);
}
/**
* Get critical severity rules only
*/
public static function getCriticalRules(): array
{
return array_filter(
self::getAllRules(),
fn (Rule $rule) => $rule->severity === DetectionSeverity::CRITICAL
);
}
/**
* Get high-priority rules (priority >= 80)
*/
public static function getHighPriorityRules(): array
{
return array_filter(
self::getAllRules(),
fn (Rule $rule) => $rule->priority >= 80
);
}
}