Safeguard
Application Security•

CWE-863 Incorrect Authorization: Why a Check Still Fails

The authorization check exists but is wrong. CWE-863 explained with case sensitivity bugs, unchecked return values and AI platform examples from real CVEs.

Safeguard Security Team
4 min read

CWE-863, Incorrect Authorization, covers the case where a product does perform an authorization check when someone tries to access a resource or act, but does not perform it correctly. Compare CWE-862, where the check is missing entirely. Here a developer did the work, and the work has a hole.

Anyone who ships roles, permissions, tenants or API scopes can hit it.

How it happens

Authorization code is full of small decisions: which identifier is compared, how strings are matched, what happens on an error. A mistake in any of them can turn a working check into a bypass.

The observed CVEs in MITRE's record show how varied the mistakes are:

  • CVE-2025-24839 (details): a collaboration platform let an attacker reach an AI bot by using a plugin to set a critical property.
  • CVE-2025-32796: an LLM application development platform allowed non-admin users to enable or disable apps through certain API endpoints.
  • CVE-2021-39155: a chain in a microservice integration platform that compared the hostname in the HTTP Host header in a case sensitive way, which allowed bypass of the authorization policy.
  • CVE-2019-15900: a chain where an sscanf call checked whether a username and group existed, but its return value was not checked. An uninitialized variable was then tested, and the check returned the wrong answer.

The last two are instructive. Neither is a missing rule. One is a string comparison detail and the other is an ignored error.

A small example of a check that looks right:

// Vulnerable: trusts a role sent by the client
app.post("/apps/:id/disable", requireLogin, (req, res) => {
  if (req.body.role === "admin") {
    disableApp(req.params.id);
    return res.sendStatus(200);
  }
  res.sendStatus(403);
});

The check runs, but it asks the wrong source. The role must come from the server side session or database.

// Fixed: use server-side identity
app.post("/apps/:id/disable", requireLogin, (req, res) => {
  if (!req.user.roles.includes("admin")) return res.sendStatus(403);
  disableApp(req.params.id);
  res.sendStatus(200);
});

What an attacker gains

MITRE lists five impacts:

  • Confidentiality: bypass restrictions to read data, directly from a store that is not correctly restricted or through weakly protected privileged functions.
  • Integrity: bypass restrictions to modify data in the same ways.
  • Access control: gain privileges by reading or modifying critical data, or reaching privileged functionality.
  • Code execution: use elevated privileges to run unauthorized commands or code.
  • Availability: consume resources until denial of service.

How to find it

MITRE's guidance on detection is candid about limits:

  • Automated static analysis is useful for common authorization idioms and related configuration, but has difficulty with custom schemes and cannot confirm a custom check cannot be bypassed. Effectiveness is limited.
  • Automated dynamic analysis may not find interfaces protected by authorization checks, even when the checks are flawed. It finds missing checks better than wrong ones.
  • Manual analysis, such as penetration testing, threat modeling and interactive tools that let testers record and modify an active session, is where wrong checks tend to surface.

Practical tests: take a request from a low privilege account and replay it with changed identifiers, changed casing in hostnames and paths, extra or missing headers, and fields the client should not control. Write a test for each rule, with a case that must be denied.

How to fix it

The MITRE record lists no formal mitigations for this entry. The mitigations for the missing authorization sibling apply here too, and are sensible practice:

  1. Divide the product into anonymous, normal, privileged and administrative areas, and enforce roles at the boundaries.
  2. Make checks reflect business logic, not just generic resource rules.
  3. Use a vetted authorization library or framework rather than hand written checks.
  4. Take identity and role from server side state, never from the request.
  5. Compare identifiers in a normalized form, and fail closed. If a lookup or parse returns an error, deny.
  6. Use a default deny policy.

Quick check

How is this different from missing authorization?

A check exists here but it is flawed. In the missing case there is no check at all.

What went wrong in the sscanf chain?

The return value was not checked, so an uninitialized variable was tested and the authorization result was wrong.

Why might a dynamic scanner miss this?

MITRE notes it may not find interfaces that are protected by checks, even when the checks have weaknesses. A manual test with real roles is needed.

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.