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.
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.
Define the dropdown selector for the 3 companies with the following Blazor code snippet:
This results in the following HTML code (and should already give an idea how this can be abused)
When one of the dropdown items is selected by the user, the "CompanySelectionCallback" is triggered in c# code.
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:
Analyzing this in a proxy tool, we can now see the modified Guid sent to the server:
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:
There is no Guid to be found that can be modified, the only thing we see in our proxy tool is an eventHandlerId:
Deep inside the AspNetCore internals this eventHandlerId is mapped to the actual Company guid:
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.