Safeguard
Application Security•

CWE-551 Authorization Before URL Canonicalization: Bypass Explained

Checking access on a raw URL before normalizing it lets attackers slip past rules with dot segments and encodings. How CWE-551 works and how to order the checks.

Safeguard Security Team
3 min read

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:

  1. Decode and canonicalize URL inputs to the application's current internal representation before validating them and checking authorization.
  2. 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.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.