Search code examples
securityserverblazor

Blazor Server - how to safeguard Events properly?


The Microsoft documentation "Threat mitigation guidance for ASP.NET Core Blazor Server", Section "Events" says: "Events provide an entry point to a Blazor Server app. The same rules for safeguarding endpoints in web apps apply to event handling in Blazor Server apps. A malicious client can send any data it wishes to send as the payload for an event"

Please help me understand this. Assume we are on Blazor server (NOT webassembly), there is ASP.Net Core Identity in place, and all Blazor pages are secured with either @attribute [Authorize] or <AuthorizeView...> tags. So authentication and authorization are in place, but additionally we need to make sure that certain users only see certain data from the database.

case #1, a Blazor component with a parameter

This parameter is supplied by it's parent page. When loading the component, a database call is made to fetch data based on the parameter:

[Parameter] public Guid companyGuid { get; set; }

protected override async Task OnInitializedAsync()
{
    using var context = DataService.CreateDbContext();
    var company = await DataService.GetCompanyByGuid(companyGuid, context);
}

case #2, a dropdown shows a selection value to choose from

The datasource is a list prefilled based on the users permissions. When the dropdown change event triggers, data is fetched from the database based on the new selection:

<RadzenDropDown Data=@DataSourceCompanies TValue="Company" 
@bind-Value="@selectedCompany"
Change="@(args => CompanySelectionChanged(args as Company))" />

List<Company> DataSourceCompanies = await GetCompaniesAndObserveUserPermissions(userGuid);

async void CompanySelectionChanged(Company c)
{
    using var context = DataService.CreateDbContext();
    var company = await DataService.GetCompanyByGuid(c.guid, context);
}

Question: Can a user modify either the [Parameter] in case 1, or the selected item in the dropdown in case 2 in a way it was not intended?

To put it differently, do i need to safeguard the database query "GetCompanyByGuid" additionally by doing something like this:

var authState = await authenticationStateTask;
string? userId = authState.User.FindFirst(c => c.Type.Contains("nameidentifier"))?.Value;
var company = await DataService.GetCompanyByGuid(c.guid, userId, context);

(and then use the userId inside the database query to additionally safeguard the query)

While this sounds "more secure" to me, it feels really cumbersome. There are 100's of places where database calls are made throughout the app. I'd like to confirm if that additional code is actually necessary?

thanks!

I read the Microsoft documentation but i'm not fully clear on what it says. Tried browser debugging mode to inspect the app, i only see HTML/css there (as expected). No internal application data that would allow me to modify the app's behaviour is visible, e.g. the company guids. But i'm too new to SignalR so there is probably still a way to abuse this. Hence my question.


Solution

  • I'm going to answer my own question after asking a professional web pen tester for help. I think the best summarization to my question is "it depends".

    Let's take the dropdown example, and analyze it step-by-step:

    Assume we have a list of 5 companies in the database, identified by a Guid. 3 companies should be visible to the user in the UI.

    enter image description here

    Define the dropdown selector for the 3 companies with the following Blazor code snippet:

    enter image description here enter image description here

    This results in the following HTML code (and should already give an idea how this can be abused)

    enter image description here

    When one of the dropdown items is selected by the user, the "CompanySelectionCallback" is triggered in c# code.

    enter image description here

    So how can this be abused? By opening F12 dev-tools in the browser and replacing one of the option values to a Guid of one of the hidden companies:

    enter image description here

    Analyzing this in a proxy tool, we can now see the modified Guid sent to the server:

    enter image description here

    Therefore, in this case the code needs to be additionally safeguarded against malicious modification.

    Now consider the same example, but instead of using a select, use the RadzenDropDown Component from the Radzen Blazor library. The generated html looks different:

    enter image description here

    There is no Guid to be found that can be modified, the only thing we see in our proxy tool is an eventHandlerId:

    enter image description here

    Deep inside the AspNetCore internals this eventHandlerId is mapped to the actual Company guid:

    enter image description here

    Since only those companies that are presented to the user are in this internal mapping list, it is not possible to maliciously access data from other company objects.

    Therefore, in this case the code is sufficiently safeguarded against modification through outside interaction.

    Hope this analysis is correct and helps others.