403 Forbidden Error for URLs with Encoded Question Marks (Apache)
Problem Statement
After recent Apache Web Server updates, many applications started returning 403 Forbidden errors when URLs contained an encoded question mark (%3F
) in GET parameters. This commonly occurs when using PHP's rawurlencode()
on user input that contains question marks, as demonstrated in this code snippet:
<!-- Returns 403 if $test contains "?" -->
<a href='test?a=<?=rawurlencode($test);?>'>test</a>
This issue stems from Apache security updates designed to mitigate critical vulnerabilities. When applications pass URL-encoded question marks (%3F
) in query parameters, Apache may interpret the request as malicious and block it with a 403 error.
Root Cause
The behavior is a security measure introduced in Apache versions 2.4.52+ to address two vulnerabilities:
- CVE-2024-38474 (Potential directory traversal attacks using
%3F
) - CVE-2024-38475 (CVSS 9.1/10 severe vulnerability)
Apache now blocks URLs containing %3F
as default behavior to prevent exploit attempts. This breaks URLs built using PHP's rawurlencode()
when the content includes question marks.
Recommended Solutions
1. Upgrade Apache and Restructure URLs (Secure Approach)
Best Practice: Upgrade to Apache 2.4.61+, then eliminate %3F
from URLs.
<?php
// Before: dangerous pattern
$returnUrl = rawurlencode($_GET['return_url']);
// After: safe alternative
$returnPath = rawurlencode(parse_url($_GET['return_url'], PHP_URL_PATH));
$returnParams = http_build_query(['size' => 'large']); // Example params
?>
<a href="login?return_path=<?= $returnPath ?>&return_params=<?= $returnParams ?>">
2. Modify Rewrite Rules (Temporary Workaround)
If immediate URL restructuring isn't feasible, adjust your .htaccess
RewriteRules in one of these ways:
# ❌ Problematic rule (triggers 403)
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L]
# ✅ Solution 1 - Remove query from substitution
RewriteRule ^(.*)$ index.php [L]
# ✅ Solution 2 - Add UnsafeAllow3F flag (security risk!)
RewriteRule ^(.*)$ index.php?route=$1 [QSA,L,UnsafeAllow3F]
WARNING
UnsafeAllow3F
re-enables vulnerable behavior. Only use this as a short-term mitigation until you finish URL refactoring.
3. Character Replacement Workaround
When refactoring isn't possible, replace question marks before encoding:
<?php
// Replace ? with HTML entity before encoding
$safeParam = rawurlencode(str_replace('?', '?', $unsafeInput));
?>
<a href="test?a=<?= $safeParam ?>">Link</a>
Explanation and Best Practices
Security Considerations
DANGER
Using UnsafeAllow3F
leaves systems vulnerable to exploitation. Attack vectors will become public at Black Hat USA 2024 - upgrade before then.
Recommended Long-Term Strategy
Patches First
- Upgrade to Apache 2.4.61+
bash# Ubuntu/Debian sudo apt update && sudo apt install apache2
Restructure URLs
- Avoid passing encoded URLs as parameters
- Use distinct param pairs (
return_path
andreturn_query
) - Limit
rawurlencode()
to path components only
Monitor Logs Check for errors like:
AH10508: Unsafe URL with %3f URL rewritten without UnsafeAllow3F
Host-Specific Notes
- Shared hosts like Strato may require support intervention
- Frameworks like Symfony/CraftCMS have patched default
.htaccess
files - Always test rewrite rule changes in staging environments
Conclusion
The 403 errors stem from necessary security enhancements in Apache. While temporary fixes like UnsafeAllow3F
or character replacement exist, the secure solution involves upgrading to Apache 2.4.61+ and restructuring applications to avoid passing %3F
in URLs. Prioritize eliminating URL-encoded question marks from parameters to maintain security compliance and application stability.