Is /reports the same resource as /reports/? To a person, nearly. To a router, an access rule and a file system, it depends. CWE-49 is path equivalence using a trailing slash, filedir/, accepted without appropriate validation.
It belongs under CWE-41. MITRE lists web based platforms, and the examples are web servers.
How it happens
A rule is attached to one form of the path. Authentication might be configured for /private/file.html while the request arrives as /private/file.html/. A server may still resolve the request to the same file, so the rule never applies. The record lists these cases:
- An application server allowed remote attackers to read source code for
.jspfiles by appending a/to the requested URL (CVE-2001-0446). - A bypass of Basic Authentication for files using a trailing
/(CVE-2004-0334). - Reading sensitive files with a trailing
/(CVE-2001-0893). - CVE-2002-0253, noted as overlapping with an information leak.
The shared cause is a gap between route matching, access control and file resolution. Each layer has its own idea of what the path is.
# Vulnerable: exact string match on the protected route
PROTECTED = {"/admin/export.csv"}
if request.path in PROTECTED and not user.is_admin:
abort(403)
# "/admin/export.csv/" is not in the set but may still resolve
What an attacker gains
MITRE lists reading and modifying files and directories. The record shows source disclosure and authentication bypass, so expect exposed code and exposed data behind a login.
How to find it
Request every protected route with a trailing slash in a test environment and compare to the plain request. If the response differs in status or content, you have found a gap. Also check what your framework does with strict slash settings, as some redirect and some silently match both.
How to fix it
- Normalize the path in one place, early, and make every later check use the normalized value.
- Protect by prefix or by directory, not by exact string, so variants fall under the same rule.
- Deny by default. Require authentication for the whole area and open specific routes on purpose.
- Keep source and secrets outside the document root.
from posixpath import normpath
path = normpath(request.path) # collapses trailing and repeated slashes
if path.startswith("/admin") and not user.is_admin:
abort(403)
A free scan at gold.safeguard.sh/scan checks dependency files for known issues in your servers and frameworks, with no login.
Quick check
Why is protecting an exact path risky?
Variants such as a trailing slash do not match the exact string yet may resolve to the same file, so the rule is skipped.
What is a safer rule shape?
A prefix or directory based rule applied to the normalized path, with deny as the default.