Closed javiercn closed 11 months ago
Blazor web combines three different models in one for writing interactive web applications. Traditional server side rendering, which is a request/response model based on HTTP. Interactive Server Side rendering, which is a rendering model based on SignalR. And finally, client side rendering, which is a rendering model based on WebAssembly.
All the general security considerations defined for the interactive rendering modes apply to Blazor web applications when there are interactive components on the application rendering in one of the given supported render modes. In the following sections we will center on the security considerations that are specific to non interactive Server Renderer Blazor web applications as well as the specific aspects that apply when different render modes interact with each other.
The Server Side rendering model is based on the traditional request/response model of HTTP. As such, there are common areas of concern with regards to security consideration and specific threats that must be taking into account and mitigated successfully. The framework provides built-in mechanims for some of these threats, but other threats are specific to application code and must be handled by the app. These threats can be categorized as follows:
Authentication and authorization: The application must ensure that the user is authenticated and authorized to access the application and the resources it exposes. The framework provides built-in mechanisms for authentication and authorization, but the application must ensure that the mechanisms are properly configured and used. The built-in mechanisms for authentication and authorization are covered elsewhere, so they won't be covered here.
Input validation and sanitization: All input coming from a client must be validated and sanitized before being used. Otherwise, the app might be exposed to attacks such as SQL injection, cross-site scripting, cross-site request forgery, open redirection, etc. The input might come from anywhere in the request.
Session management: Properly managing user sessions is critical to ensure that the application is not exposed to attacks such as session fixation, session hijacking, etc. Information that is stored in the session must be properly protected and encrypted and sessions can't be guessed or manipulated.
Error handling and logging: The application must ensure that errors are properly handled and logged. Otherwise, the application might be exposed to attacks such as information disclosure, etc. This happens for example, when the application returns sensitive information in the response or when the application returns detailed error messages that can be used to attack the application.
Data protection: Sensitive data must be properly protected, this includes application logic when running on webassembly, since it can be easily reverse engineered.
Denial of service: The application must ensure that it is not exposed to attacks such as denial of service, etc. This happens for example, when the application is not properly protected against brute force attacks, when an action can cause the application to consume too many resources,
All input coming from the client must be considered untrusted unless its information that was generated and protected on the server (like a CSRF token or any other payload protected with authenticated encryption, like an auth cookie or a session identifier).
Input is normally available to the app through a binding process, for example via [SupplyParameterFromQuery]
or [SupplyParameterFromForm]
. Before processing this input, we need to make sure that it is valid. For example, we need to make sure that there were no binding errors when mapping the form data to a component property or otherwise we might be processing invalid data.
In the same way, if the input is used to perform a redirect, we need to make sure that the input is valid and that it is not pointing to a domain we consider valid, or to a subpath within the app base path. Otherwise, we might be exposing the application to open redirection attacks, where an attacker can craft a link that redirects the user to a malicious site.
Finally, if the input is used to perform a database query, we need to make sure that the input is valid and that it is not exposing the application to SQL injection attacks. Otherwise, an attacker might be able to craft a malicious query that can be used to extract information from the database or to modify the database.
Data that might have come from input also needs to be sanitized before included in a response. For example, the input might contain HTML or JavaScript that can be used to perform cross-site scripting attacks, that can be used to extract information from the user or to perform actions on behalf of the user.
The framework provides the following mechanisms to help with input validation and sanitization:
OnValidSubmit
callback and avoids doing so if there are any binding errors.It is important that all input and permissions are validated server side at the time of performing a given action to ensure that the data is valid and accurate at that time and that the user is allowed to perform the action. This approach is consistent with the guidance offered in interactive Blazor Server rendering.
Session management is handled by the framework. The framework uses a session cookie to identify the user session. The session cookie is protected using the Data Protection APIs. The session cookie is not accessible to JavaScript code running on the browser and is not guessable nor manipulable.
With regards to other session data that might be for example, stored within services, it is fundamental that the session data is stored within scoped services, as those are unique per a given user session, as opposed to singleton services which are shared across all user sessions in a given process instance. When it comes to SSR, there's no much difference between scoped and transient services in most cases as the lifetime of the service is limited to a single request, but it makes a different in two scenarios:
The framework provides built-in logging for the application at the framework level. The framework logs important events like when the antiforgery token for a form fails to validate, when a root component starts to render, when an action is dispatched, etc. The app is responsible for logging any other events that might be important to the app.
The framework provides built-in error handling for the application at the framework level. The framework handles errors that happen during the rendering of a component and either uses the error boundary mechanism to display a friendly error message or lets the error bubble up to the exception handling middleware which is configured to render the error page.
Errors that happen during streaming rendering after the response have started to be sent to the client are displayed in the final response as a generic error message. Details about the cause of the error are only included during development.
The framework offers mechanisms for protecting sensitive information for a given user session and ensures that the built-in components use these mechanisms to protect sensitive information is aware of, like the user identity when using cookie authentication. Besides that, the app is responsible for protecting any other app specific information. The most common way of doing this is via the data protection APIs or any other form of encryption. As a general rule, the app is responsible for:
With regards to data protection it is also important to clearly understands what code executes where and what the implications for that are. In the server and interactive server mode, the code is stored on the server and never reaches the client. In web assembly, the application code will always reach the client, which means that any sensitive information that is stored in the application code will be available to anyone with access to the app. No matter whether the app uses offuscation or any other similar technique to "protect" the code, once the code reaches the client, it can be reverse engineered and the information extracted.
The framework at the server level provides limits on aspects like the max size of the request, header size, etc. With regards with application code, our form mapping system defines limits similar to those defined by MVC's model binding system:
Limit on the max number of elements bound in a collection.
In addition to that, there are limits defined for the form, like the max form key and value size, max number of entries, etc.
In general, the application must evaluate when there is a chance that a request triggers an asymmetric amount of work by the server. Example of this are:
This aspect has more to do with the difference in growth between the work the client performs and the work the server performs than with a specific 1->N comparison. For example, it can be that a client might submit a workitem (inserting elements into a list) that takes N units of time to perform, but the server needs N^2 to process (because it might be doing something very naive). It's the difference between N and N^2 that matters.
As such, there's a limit on how much "work" the server must be willing to do, which is specific to the app. This aspect applies to Blazor Server at all, since the resources are on the server, but does not necessarily apply to webassembly in most situations.
The other important aspect is that this is not only reserved to CPU time, but it applies to any resources (memory, network, space on disk, etc).
With regards to webassembly, there's normally not an issue with regards to the amount of work the client performs, since the client is normally limited by the resources available on the client. However, there are some scenarios where the client might be impacted by this, if for example, an app displays data from other users and one user is capable of adding data to the system that forces the clients that display the data to perform an amount of work that is not proportional to the amount of data added by the user.
@guardrex can you put this in the line to add it to the docs?
Using the referenced issue to track it.