Broken access control
- 4 minutes
Recall that you recently joined a team at an IT software company who tasked you with conducting a design and code review of the team-owned codebases. As you onboard to your new team and explore the codebase, you discover an ASP.NET Blazor web project. With OWASP Top 10 in mind, you set off on a deep dive into the code with your security lenses on.
You start at the top of the OWASP Top 10 list with #1: Broken Access Control. This category refers to incidents where a user who shouldn’t have permission to access that data viewed confidential information.
Built-in framework security capabilities
.NET has built-in authentication and session management, so there's no need to implement your own.
Let's consider a ASP.NET Core controller. A controller without any authorization attributes treats each request the same way without applying any security checks. By decorating controller actions or the controller itself with Authorize
(user must be signed in and authenticated) or AllowAnonymous
(any unauthenticated caller can invoke method) attributes, you gain control over what's publicly accessible and which functionality is for authorized users only.
Plain ASP.NET controller with no authorization attributes, no access restrictions applied.
public class AccountController : Controller
{
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
public ActionResult<string> GetCitizenTaxId()
{
}
}
Controller with authorization attributes, based on policy or role assignments. Authorized caller is able to invoke the GetCitizenTaxId
method.
[Authorize(Policy="", Roles=""]
public class AccountController : Controller
{
[AllowAnonymous]
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
[Authorize]
public ActionResult<string> GetCitizenTaxId()
{
}
}
Similarly, the ASP.NET Minimal API supports the attribute decoration (Lambda HTTP get method with [Authorize]
attribute), policy (AdminsOnly
), and claim (admin
) authorization, as shown here:
var builder = WebApplication.CreateBuilder(args);
// Policy and claim use below
builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", b => b.RequireClaim("admin", "true")));
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<ApplicationDbContext>();
var app = builder.Build();
app.UseAuthorization();
// Attribute use below
app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization.");
app.MapGet("/", () => "This endpoint doesn't require authorization.");
app.Run();
Your application's user interface should also reflect the user's authentication (the user is who they say they are) and authorization state (whether the user is allowed to access certain information). Here, too, you're covered by the OWASP Top 10 framework. ASP.NET Blazor's razor syntax supports conditionally displayed components depending on authorization status. The AutorizeView
component selectively displays UI content based on user's authorized status.
<AuthorizeView Roles="admin, superuser">
<Authorized>
<h1>Hello, @context.User.Identity.Name!</h1>
<p>You can only see this content if you are authorized.</p>
<button @onclick="SecureMethod">Authorized Only Button</button>
</Authorized>
<NotAuthorized>
<h1>Authentication Failure!</h1>
<p>You are not signed in.</p>
</NotAuthorized>
</AuthorizeView>
@code {
private void SecureMethod()
{
// Invoked only upon successful authorization
}
}
The SecureMethod
is accessible once the user is authorized. Because the AutorizeView
component can validate against roles or policies, only the role claims for either the admin or superuser roles would have the button.
Code review notes
You and your team considered the broken access control risk and implemented Claims-based and Policy-based authorization in your web app. Knowing the app gets deployed to Azure, other best practices include:
- Authorizing users on all externally facing endpoints.
- Using role-based and policy-based authorization in your application. ASP.NET has many ways to authorize a user based on their role or claims.