Overview
CVE-2005-4398 describes a cross-site scripting vulnerability in lemoon 2.0 and earlier. The vendor disputed the issue, stating that the XSS vulnerability existed on a public site built on lemoon and not in the core product. This example underscores how insecure handling of user-controlled input can lead to browser-executable scripts when rendering content. While that CVE is not a Broken Object Level Authorization (BOLA) issue in ASP.NET Core, it illustrates the broader risk of failing to enforce proper input handling and authorization boundaries in web applications. In modern ASP.NET Core applications, BOLA occurs when endpoints take an object identifier from the client and return or modify that object without validating that the current user is authorized to access it, enabling unauthorized access, data leakage, or manipulation across users.
Affected Versions
lemoon 2.0 and earlier
Code Fix Example
ASP.NET Core API Security Remediation
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Demo
{
// Simple resource model with an owner
public class Resource { public int Id { get; set; } public string OwnerId { get; set; } }
public class AppDbContext : DbContext { public DbSet<Resource> Resources { get; set; } }
// Per-resource authorization requirement and handler
public class ResourceReadRequirement : IAuthorizationRequirement { }
public class ResourceAuthorizationHandler : AuthorizationHandler<ResourceReadRequirement, Resource>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ResourceReadRequirement requirement, Resource resource)
{
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (!string.IsNullOrEmpty(userId) && resource.OwnerId == userId)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
// Controller demonstrating vulnerable vs. fixed patterns
[ApiController]
[Route("api/[controller]")]
public class ResourcesController : ControllerBase
{
private readonly AppDbContext _db;
private readonly IAuthorizationService _authorizationService;
public ResourcesController(AppDbContext db, IAuthorizationService authorizationService)
{
_db = db; _authorizationService = authorizationService;
}
// Vulnerable pattern: returns resource by id without per-object authorization
[HttpGet("vulnerable/{id}")]
public async Task<IActionResult> GetResourceVulnerable(int id)
{
var res = await _db.Resources.FindAsync(id);
if (res == null) return NotFound();
return Ok(res); // No authorization check on the object
}
// Fixed pattern: enforce per-object authorization before returning the resource
[HttpGet("fixed/{id}")]
public async Task<IActionResult> GetResourceFixed(int id)
{
var res = await _db.Resources.FindAsync(id);
if (res == null) return NotFound();
var authorized = await _authorizationService.AuthorizeAsync(User, res, "Read");
if (!authorized.Succeeded) return Forbid();
return Ok(res);
}
}
// Wiring configuration (e.g., in Startup.cs or Program.cs)
// public void ConfigureServices(IServiceCollection services)
// {
// services.AddControllers();
// services.AddDbContext<AppDbContext>(/* options */);
// services.AddAuthorization(options =>
// {
// options.AddPolicy("Read", policy => policy.Requirements.Add(new ResourceReadRequirement()));
// });
// services.AddSingleton<IAuthorizationHandler, ResourceAuthorizationHandler>();
// }
}