CWE-551, Incorrect Behavior Order: Authorization Before Parsing and Canonicalization, is a bypass caused by doing the right checks in the wrong order. MITRE says that if a web server does not fully parse requested URLs before it examines them for authorization, an attacker may be able to bypass authorization protection.
How it happens
A URL can be written many ways that mean the same thing. MITRE's example: the strings /./ and / both mean the current directory. Suppose /SomeDirectory is protected, and the access rule matches the literal text /SomeDirectory. An attacker requests /./SomeDirectory. The rule does not match the text, so no restriction applies. Later, the server resolves the path, turns it into /SomeDirectory, and serves the protected resource.
The check and the use looked at two different forms of the same request.
# Vulnerable: authorization runs on the raw path
def handle(request):
if request.path.startswith("/admin") and not user_is_admin(request):
return deny()
path = normalize(request.path) # canonicalized too late
return serve(path)
Requesting /./admin/users or a path with encoded characters can sail past the first test and still resolve to the protected area.
# Fixed: canonicalize first, authorize the result
def handle(request):
path = normalize(decode_once(request.path))
if path.startswith("/admin") and not user_is_admin(request):
return deny()
return serve(path)
What an attacker gains
MITRE lists bypassing a protection mechanism as the impact. Access control that works for the normal spelling of a URL fails for an equivalent spelling, so protected pages and files become readable.
How to find it
- Test every protected path with alternative spellings: dot segments, doubled slashes, encoded characters and mixed case, where case matters to the rule.
- Compare the response for each spelling with the response for the plain URL. Any difference is a lead.
- Review where in the request pipeline authorization runs relative to URL decoding and normalization.
- Check whether the proxy in front and the application behind it interpret paths the same way.
How to fix it
MITRE's guidance for this entry:
- Decode and canonicalize URL inputs to the application's current internal representation before validating them and checking authorization.
- Make sure the application does not decode the same input twice. A second decode can let dangerous input in after it was checked, which defeats allowlist schemes.
Add to that: make the authorization decision on the same value that gets used to find the resource, and prefer deny-by-default rules that apply to a whole subtree after normalization.
Quick check
Why is decoding twice a problem?
An input can pass the check in its once-decoded form and then turn into something dangerous on the second decode. MITRE warns this can bypass allowlists.
Is blocking the string dot-slash enough?
No. There are many equivalent spellings. Normalize first, then check the result.