Apache中Location与LocationMatch的优先级问题及路径遍历防护规则失效排查
Hey there, let's break down why your path traversal protection rule is working for some of your Location blocks but getting ignored for others. The core issue here boils down to how Apache processes Location vs. LocationMatch directives in order, and how that interacts with your ProxyPass and Rewrite rules.
First, Let's Clarify Apache's Directive Processing Order
Apache handles request matching in a specific sequence that's key to understanding your problem:
- Prefix-matching
<Location>directives (like<Location "/AXIS/">or<Location "/WS/">) are processed first. Among these, Apache uses the longest path match (so/rest/takes priority over/if the request starts with/rest/). - Regex-matching directives (like your
<LocationMatch "\.\.;">) are processed after all prefix-matching Location blocks.
The critical thing here is: once Apache executes a directive that forwards or terminates the request (like ProxyPass), any subsequent rules (including your LocationMatch) won't run at all. That's exactly what's happening with your /WS/ and /rest/ blocks.
Why Your Current Rule Gets Ignored
Your <LocationMatch "\.\.;"> rule is meant to block requests containing ../; (the pattern linked to the Tomcat path traversal issue), but here's the problem:
- For requests hitting
/WS/or/rest/, Apache first processes the entire<Location "/WS/">/<Location "/rest/">block. This includes running your internal Rewrite rules and then executingProxyPass, which immediately forwards the request to Tomcat. By the time Apache would get to your LocationMatch rule, the request is already gone—so the protection never kicks in. - For
/AXIS/, you mentioned it doesn't expose the issue. This is likely just coincidence: either the AXIS application itself doesn't have the vulnerable path handling, or you haven't tested it with the exact malicious request pattern. Under the hood, the same priority issue applies—your LocationMatch rule isn't protecting/AXIS/either; it just hasn't been exploited yet.
Fixes to Ensure Your Protection Works Everywhere
Here are two reliable ways to fix this, ordered by simplicity and effectiveness:
1. Move the Protection to a Global Rewrite Rule (Best Option)
Place your path traversal protection at the VirtualHost level (before any Location blocks). VirtualHost-level Rewrite rules run before any Location matching happens, so they'll intercept malicious requests before they reach your ProxyPass directives.
Update your config like this:
<VirtualHost *:80> # Global path traversal protection - runs FIRST RewriteEngine On # Block requests containing ../; (and URL-encoded variants like %2e%2e%3b) RewriteRule "(?:\.\.;|%2e%2e%3b)" / [L,R=403,NC] # Your existing Location blocks go here <Location "/AXIS/"> ProxyPass ajp://127.0.0.1:8009/AXIS/ ProxyPassReverse ajp://127.0.0.1:8009/AXIS/ </Location> <Location "/WS/"> RewriteEngine On RewriteCond %{ENV:X_SESSIONREF} (.+) RewriteRule . - [E=SREF:%1,NS] RequestHeader set X_SESSIONREF "%{SREF}e" env=SREF RewriteCond %{LA-U:REMOTE_USER} (.+) RewriteRule . - [E=RU2:%1,NS] RequestHeader set REMOTE_USER "%{RU2}e" env=RU2 ProxyPass ajp://127.0.0.1:8009/WS/ ProxyPassReverse ajp://127.0.0.1:8009/WS/ </Location> <Location "/rest/"> RewriteEngine On RewriteCond %{ENV:X_SESSIONREF} (.+) RewriteRule . - [E=SREF:%1,NS] RequestHeader set X_SESSIONREF "%{SREF}e" env=SREF RewriteCond %{LA-U:REMOTE_USER} (.+) RewriteRule . - [E=RU2:%1,NS] RequestHeader set REMOTE_USER "%{RU2}e" env=RU2 RewriteCond %{ENV:X_SESSION} (.+) RewriteRule . - [E=ASSESS:%1,NS] RequestHeader set X_SESSION "%{ASSESS}e" env=ASSESS ProxyPass ajp://127.0.0.1:8009/rest/ ProxyPassReverse ajp://127.0.0.1:8009/rest/ ErrorDocument 403 default ErrorDocument 404 default ErrorDocument 405 default ErrorDocument 406 default ErrorDocument 409 default ErrorDocument 500 default ErrorDocument 502 default ErrorDocument 503 default </Location> </VirtualHost>
- The
NCflag makes the match case-insensitive (covers any encoded variants like%2E%2E%3B). - The
Lflag tells Apache to stop processing further rules once this one triggers, so the request gets blocked immediately.
2. Add Protection to Each Vulnerable Location Block
If you can't use a global rule for some reason, duplicate the protection rule inside each <Location> block that uses ProxyPass. Place it before the ProxyPass directive so it runs first:
For example, in your /WS/ block:
<Location "/WS/"> RewriteEngine On # Add this line FIRST in the block RewriteRule "\.\.;" / [L,R=403] # Your existing rules follow RewriteCond %{ENV:X_SESSIONREF} (.+) RewriteRule . - [E=SREF:%1,NS] RequestHeader set X_SESSIONREF "%{SREF}e" env=SREF RewriteCond %{LA-U:REMOTE_USER} (.+) RewriteRule . - [E=RU2:%1,NS] RequestHeader set REMOTE_USER "%{RU2}e" env=RU2 ProxyPass ajp://127.0.0.1:8009/WS/ ProxyPassReverse ajp://127.0.0.1:8009/WS/ </Location>
This ensures the protection runs before the request gets forwarded to Tomcat.
Bonus: Hardening Tips
- Enable Apache's
ProxyPassInterpolateEnv Off(default, but double-check) to prevent environment variable injection. - Ensure Tomcat's
context.xmlhasallowLinking="false"to add an extra layer of protection on the backend. - Test with both raw and URL-encoded malicious requests to confirm your rules catch all variants.
备注:内容来源于stack exchange,提问作者INS

