Fix WAF whitelist ordering and URI matching

This commit is contained in:
root
2026-01-30 22:56:06 +02:00
parent 5c73cbcd1c
commit e3bfeae23d
2 changed files with 76 additions and 10 deletions

View File

@@ -257,6 +257,7 @@ class Waf extends Page implements HasForms, HasTable
{
$ruleId = (string) ($entry['rule_id'] ?? '');
$uri = (string) ($entry['uri'] ?? '');
$uriPath = $this->stripQueryString($uri);
$host = (string) ($entry['host'] ?? '');
$ip = (string) ($entry['remote_ip'] ?? '');
@@ -281,10 +282,10 @@ class Waf extends Page implements HasForms, HasTable
if ($matchType === 'ip' && $matchValue !== '' && $this->ipMatches($ip, $matchValue)) {
return true;
}
if ($matchType === 'uri_exact' && $matchValue !== '' && $uri === $matchValue) {
if ($matchType === 'uri_exact' && $matchValue !== '' && ($uri === $matchValue || $uriPath === $matchValue)) {
return true;
}
if ($matchType === 'uri_prefix' && $matchValue !== '' && str_starts_with($uri, $matchValue)) {
if ($matchType === 'uri_prefix' && $matchValue !== '' && (str_starts_with($uri, $matchValue) || str_starts_with($uriPath, $matchValue))) {
return true;
}
if ($matchType === 'host' && $matchValue !== '' && $host === $matchValue) {
@@ -306,6 +307,7 @@ class Waf extends Page implements HasForms, HasTable
$ruleId = (string) ($entry['rule_id'] ?? '');
$uri = (string) ($entry['uri'] ?? '');
$uriPath = $this->stripQueryString($uri);
$host = (string) ($entry['host'] ?? '');
$ip = (string) ($entry['remote_ip'] ?? '');
@@ -322,10 +324,10 @@ class Waf extends Page implements HasForms, HasTable
if ($matchType === 'ip' && $matchValue !== '' && $this->ipMatches($ip, $matchValue)) {
return true;
}
if ($matchType === 'uri_exact' && $matchValue !== '' && $uri === $matchValue) {
if ($matchType === 'uri_exact' && $matchValue !== '' && ($uri === $matchValue || $uriPath === $matchValue)) {
return true;
}
if ($matchType === 'uri_prefix' && $matchValue !== '' && str_starts_with($uri, $matchValue)) {
if ($matchType === 'uri_prefix' && $matchValue !== '' && (str_starts_with($uri, $matchValue) || str_starts_with($uriPath, $matchValue))) {
return true;
}
if ($matchType === 'host' && $matchValue !== '' && $host === $matchValue) {
@@ -365,8 +367,9 @@ class Waf extends Page implements HasForms, HasTable
public function whitelistEntry(array $record): void
{
$rules = $this->getWhitelistRules();
$matchType = 'uri_exact';
$matchValue = (string) ($record['uri'] ?? '');
$matchType = 'uri_prefix';
$rawUri = (string) ($record['uri'] ?? '');
$matchValue = $this->stripQueryString($rawUri);
if ($matchValue === '') {
$matchType = 'ip';
$matchValue = (string) ($record['remote_ip'] ?? '');
@@ -417,6 +420,16 @@ class Waf extends Page implements HasForms, HasTable
->send();
}
protected function stripQueryString(string $uri): string
{
$pos = strpos($uri, '?');
if ($pos === false) {
return $uri;
}
return substr($uri, 0, $pos);
}
public function removeWhitelistEntry(array $record): void
{
$rules = $this->getWhitelistRules();

View File

@@ -799,7 +799,11 @@ function wafAuditLogList(array $params): array
$entry['severity'] = $matches[1];
}
if (preg_match('/\\[uri "([^"]+)"\\]/', $line, $matches)) {
$entry['uri'] = $matches[1];
$loggedUri = $matches[1];
$currentUri = (string) ($entry['uri'] ?? '');
if ($currentUri === '' || (!str_contains($currentUri, '?') && $loggedUri !== '')) {
$entry['uri'] = $loggedUri;
}
}
if (preg_match('/\\[hostname "([^"]+)"\\]/', $line, $matches)) {
$entry['host'] = $matches[1];
@@ -3094,6 +3098,50 @@ function findWafBaseConfig(): ?string
return null;
}
function findWafCoreConfig(): ?string
{
$paths = [
'/etc/modsecurity/modsecurity.conf',
'/etc/modsecurity/modsecurity.conf-recommended',
'/etc/nginx/modsecurity.conf',
];
foreach ($paths as $path) {
if (file_exists($path) && isWafBaseConfigUsable($path)) {
return $path;
}
}
return null;
}
function buildWafCrsIncludeLines(): array
{
$lines = [];
if (file_exists('/etc/modsecurity/crs/crs-setup.conf')) {
$lines[] = 'Include /etc/modsecurity/crs/crs-setup.conf';
} elseif (file_exists('/usr/share/modsecurity-crs/crs-setup.conf')) {
$lines[] = 'Include /usr/share/modsecurity-crs/crs-setup.conf';
}
if (file_exists('/etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf')) {
$lines[] = 'Include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf';
}
if (is_dir('/etc/modsecurity/crs/rules')) {
$lines[] = 'Include /etc/modsecurity/crs/rules/*.conf';
} elseif (is_dir('/usr/share/modsecurity-crs/rules')) {
$lines[] = 'Include /usr/share/modsecurity-crs/rules/*.conf';
}
if (file_exists('/etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf')) {
$lines[] = 'Include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf';
}
return $lines;
}
function isModSecurityModuleAvailable(): bool
{
$output = [];
@@ -3186,15 +3234,15 @@ function wafApplySettings(array $params): array
}
ensureWafUnicodeMapFile();
$baseConfig = findWafBaseConfig();
if (!$baseConfig) {
$coreConfig = findWafCoreConfig();
if (!$coreConfig) {
file_put_contents(JABALI_WAF_INCLUDE, "# Managed by Jabali\nmodsecurity off;\n");
return ['success' => false, 'error' => 'ModSecurity base configuration not found'];
}
$rules = [
'# Managed by Jabali',
'Include "' . $baseConfig . '"',
'Include "' . $coreConfig . '"',
'SecRuleEngine On',
'SecAuditEngine ' . ($auditLog ? 'On' : 'Off'),
'SecAuditLog /var/log/nginx/modsec_audit.log',
@@ -3207,6 +3255,11 @@ function wafApplySettings(array $params): array
$rules = array_merge($rules, $whitelistLines);
}
$crsIncludes = buildWafCrsIncludeLines();
if (!empty($crsIncludes)) {
$rules = array_merge($rules, $crsIncludes);
}
file_put_contents(JABALI_WAF_RULES, implode("\n", $rules) . "\n");
$include = [