Overview
Broken Function Level Authorization (BFLA) occurs when a web API trusts a user to choose which function or operation to execute without validating permissions for each function. In ASP.NET Core this can look like taking an operation name from a query string and mapping it to a method via reflection or an internal registry, effectively letting a non-admin user trigger restricted behavior.
CVE-2005-4398 describes a cross-site scripting vulnerability affecting lemoon 2.0 and earlier, where untrusted input could be reflected into a page. The vendor dispute notes the issue resided in a public site built on top of lemoon core rather than the lemoon core product itself. While not an ASP.NET Core vulnerability, the CVE highlights how untrusted user input can influence server-side behavior or rendering, underscoring the risk of trusting client-supplied data when implementing dynamic function invocation or output rendering. This guidance uses that context to illustrate why per-function authorization is critical in ASP.NET Core.
In practice, attackers can pass a function name in a query, route segment, or body and cause the application to dispatch to a handler they should not access. If there is no per-operation authorization, the attacker can call restricted functions, bypass feature gates, or exfiltrate data exposed through other operations. The fix is to restrict dynamic invocation by maintaining an explicit allowlist and enforcing authorization for every operation via policies or resource-based checks, rather than relying on input alone. Use IAuthorizationService or policy attributes to enforce per-operation access before executing sensitive code, and avoid direct reflection-based calls from user input.
You should also add unit and integration tests to verify that unauthorized users cannot access restricted operations (returning 403) and that claims/roles properly gate access. Logging of failed authorization attempts and periodic reviews of the allowlist are recommended to prevent drift over time.
Affected Versions
N/A (CVE-2005-4398 relates to lemoon 2.0 and earlier, not ASP.NET Core).
Code Fix Example
ASP.NET Core API Security Remediation
Vulnerable pattern:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class OperationsController : ControllerBase
{
// Unsafe: operation name from user input is used to reflectively invoke a method
[HttpGet("execute")]
public IActionResult Execute(string operation)
{
var typeName = $"MyApp.Operations.{operation}"; // user-controlled
var t = Type.GetType(typeName);
if (t == null) return NotFound();
var m = t.GetMethod("Run");
var inst = Activator.CreateInstance(t);
var result = m?.Invoke(inst, null);
return Ok(result);
}
}
Fixed pattern:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
public class OperationRequirement : IAuthorizationRequirement
{
public string OperationName { get; }
public OperationRequirement(string operationName) => OperationName = operationName;
}
public class OperationAuthorizationHandler : AuthorizationHandler<OperationRequirement, string>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
OperationRequirement requirement, string resource)
{
// Example: require a claim like "permission" with value "execute:{operation}"
if (context.User.HasClaim("permission", $"execute:{requirement.OperationName}"))
{
context.Succeed(requirement);
}
else if (context.User.IsInRole("Admin"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
[ApiController]
[Route("api/[controller]")]
public class OperationsController : ControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IOperationService _operationService;
private static readonly HashSet<string> AllowedOperations = new HashSet<string>
{
"CreateUser", "GetReport", "ExportData"
};
public OperationsController(IAuthorizationService authorizationService,
IOperationService operationService)
{
_authorizationService = authorizationService;
_operationService = operationService;
}
[HttpGet("execute/{operation}")]
public async Task<IActionResult> Execute(string operation)
{
if (!AllowedOperations.Contains(operation))
return Forbid();
var authResult = await _authorizationService.AuthorizeAsync(User, operation, new OperationRequirement(operation));
if (!authResult.Succeeded) return Forbid();
// Explicit, safe dispatch to a known operation
switch (operation)
{
case "CreateUser": return Ok(_operationService.CreateUser());
case "GetReport": return Ok(_operationService.GetReport());
case "ExportData": return Ok(_operationService.ExportData());
default: return Forbid();
}
}
}