Blazor Server - how to safeguard Events properly? - security

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.

Question: Can a user modify either the [Parameter] in case 1
The user's state is held in the server's memory in a circuit. Examples of user state held in a circuit include:
The hierarchy of component instances and their most recent render output in the rendered UI.
The values of fields and properties in component instances.
Therefore, to change the user information, you must have access to the server's RAM, and you should not worry about this.
For transient data that the user is actively creating, a commonly used storage location is the browser's localStorage and sessionStorage collections:
localStorage is scoped to the browser's window. If the user reloads the page or closes and re-opens the browser, the state persists. If the user opens multiple browser tabs, the state is shared across the tabs. Data persists in localStorage until explicitly cleared.
sessionStorage is scoped to the browser tab. If the user reloads the tab, the state persists. If the user closes the tab or the browser, the state is lost. If the user opens multiple browser tabs, each tab has its own independent version of the data.
Third-party NuGet packages provide APIs for working with localStorage and sessionStorage. It's worth considering choosing a package that transparently uses ASP.NET Core Data Protection. Data Protection encrypts stored data and reduces the potential risk of tampering with stored data.
For more info, you can refer to ASP.NET Core Blazor state management and also Browser storage (localStorage/sessionStorage)
In general, If you'd like to have a very secure application, then you have to add a lot of such overheads like what you yourself gave an example.

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.

Related

is authentication with client side rendered app and sessions possible?

No matter how I reason about it, it seems as if there is no secure way of implementing a client side rendered single-page-application that uses/accesses information on sessions for authentication, either via cookies, without severe compromise in security. I was mainly looking to building a React app, but it seems as if I will need to build it with SSR for a relatively secure version of authentication.
The use case that I'm especially thinking of is where the user logs in or registers and then gets a cookie with the session id. From there, in a server side implementation, I can simply set up conditional rendering depending on whether the server stored session has an associated user id or not and then pull the user information from there and display it.
However, I can't think of a client-side rendered solution where the user can use the session id alone on the cookie that isn't easily spoofable. Some of the insecure implementations would include using browser storage (local/session). Thanks.
I think the major issue here is that you are mixing the two parts of a web page (at least according to what HTML set out achieve) and treating them both as sensitive information.
You have two major parts in a web page - the first being the display format and the second being the data. The presumption in client side rendering / single page applications is that the format itself is not sensitive, and only the data needs to be protected.
If that's the case you should treat your client-side redirect to login behavior as a quality of life feature. The data endpoints on your server would still be protected - meaning that in theory an unauthenticated user could muck about the static HTML he is being served and extract page layouts and templates - but those would be meaningless without the data to fill them - which is the protected part.
In practice - your end product would be a single page application that makes requests to various API endpoints to fetch data and fill in the requested page templates. You wouldn't even need to go as far as storing complex session states - a simple flag notifying the client if it is authenticated or not would suffice (that is beyond what you would normally use for server-side authentication such as cookies or tokens)
Now let's say I'm a malicious user who is up to no good - I could "spoof" - or really just open the browser dev tools and set the isAuthenticated flag to true letting me skip past the login screen - now what would I do? I could theoretically navigate to my-service/super-secret without being redirected locally back to the login page on the client side - and then as soon as the relevant page tries to load the data from the server with the nonexistent credentials it would fail - best case displaying an error message, worst case with some internal exception and a view showing a broken template.
So just to emphasize in short:
A. If what you want to protect is your TEMPLATE then there is no way to achieve this clientside.
B. If what you want to protect is your DATA then you should treat gating/preventing users from navigating to protected pages as a quality of life feature and not a security feature, since that will be implemented on the server when serving the data for that specific page.

Login session transferred to other user

I have kind of a strange problem. I have build a web application in Lucee. You need to login to use web application. It has happened, at least twice, that a login session has been transferred to an other user. To clarify what happen:
User 1 is logged in the application, the session is active
User 2 goes to the web application and is automatically logged in and sees "welcome to the application user 1".
As mentioned above this has happened at least twice since the application is live, so it sounds like an incident. Security wise this is a big problem because user 1 is an administrator and user 2 has a basic access profile.
My question: does anyone recognize this issue and can someone give my some advice how to troubleshoot this problem.
Thanks
If you are using session variables, this could happen with data being assigned to the incorrect scope in a CFC or with objects being stored incorrectly in the application scope or even a mash-up of both.
Make sure that your CFC functions are using the function local scope:
var x = "" or local.x = ""
otherwise, x will be in the variables scope of the CFC, where it can be manipulated by any function inside the CFC. This leads to data bleeding from one call to another across sessions. Try using varscoper to scan your code for these issues.
Alternately, you could store an object that contains data for a user into the application scope or inside another object which is stored in the application scope. This could allow User A to access data meant for User B when they are logged in at the same time.
You need to do an audit of your code base for issues like this and go through your user session logic to verify where and how data is stored and accessed.
Like one of the comments suggested could be the users were on the same network and/or they used a proxy such as squid which would cache all incoming content. To check to see if it's a possibility take a look at the headers being sent out by your server and see if there are any headers related to caching (Cache-Control, Expires, Last-Modified, ETag).
if you want to prevent caching you could set the first example header in you application.cfc onRequestStart or to at least prevent user content being cached you could do some variation of the second example.
<cfscript>
//EX 1
header name="cache-control" value="no-cache"; //no caching by anything
//EX 2
if(loggedIn){
header name="cache-control" value="private, max-age=<time_in_seconds>"; //allow browser to cache content
}else{
header name="cache-control" value="public, max-age=<time_in_seconds>"; //allow anything to cache content
}
</cfscript>

How secure are shared tables in CloudKit?

Imagine we are building a shared bookmarking app with the following tables:
Company (companyID)
User (userID, companyID)
Bookmark (bookmarkID, content, companyID)
A user should only be able to access the bookmarks which match their companyID.
Traditionally you would ensure this kind of security by not giving a client direct access to the database. Instead you would have a server that would authenticate the user, and the client would send a unique token to the server along with a request for Bookmarks. Once the server receives this token, it would know exactly which records in the Bookmarks table the user is allowed to access.
That's great, but what if you don't have a server, and you want to rely purely on CloudKit?
Since you don't have a server, it seems like you must allow full access to the Bookmarks table to each user. Then, the client side code would be responsible for preventing access to records that are off-limits. (E.g. When you send a retrieve Bookmark request, you would use a predicate such as companyID == 3.)
The question is how secure is a system like this which allows full access by the client, but limits it based solely on client side code?
(Note: For this question, assume an attacker jailbreaks their phone, and uses whatever means necessary to access data they aren't allowed to see. In the case where we were using a server, we don't care if they jailbreak their phone, since all requests still need to go through our servers. The only way they would be able to access data they can't see is if they found out the secret token that is assigned to another user. However, when the attacker has access to another user's phone, I'm assuming all bets are off: the attacker would be able to figure out the secret token that other client is using and then access that user's private data. Therefore, the original question I posed can ignore the case where the attacker has access to another person's phone, since even in the more secure version (with a server), all bets are off. In other words, assume the attacker has access to their own phone only for the following questions.)
Examples of things I am wondering:
Can the attacker figure out which credentials the client is using for CloudKit and then run custom queries against CloudKit?
Assuming I am using a predicate such as #"companyID == %#" and replacing the %# with the contents of a variable, can the attacker simply change the value of this variable before the query is sent?
Assuming I am using a predicate of the form #"companyID == 3", can the attacker simply change the string to #"true || companyID == 3" so that they get all records?
Can the attacker monitor network traffic and see requests that are being sent to CloudKit and then modify those using a man-in-the-middle technique?
Say I want to prevent duplicate companyIDs from being created so before I create a company, I run a query to fetch that companyID. Once I have the company stored in a local variable, I run code such as if companyID != nil { return } before I create the company. Can the attacker modify their client so that it doesn't execute this line of code? What if instead of companyID != nil, I use a boolean, companyExists. Would that make any difference?
Before I send a new bookmark to CloudKit to be saved, I run code such as bookmark[#"companyID"] = 3 to set the proper companyID. If the attacker changed this value to 4, for example, it would be a security violation. Can they do something of this nature?
Does the client matter? I'm primarily concerned with iOS, but what if I made an Android or JavaScript client? Would that make it any more/less secure?
Does the OS version matter? Should I only users to run the app if they are on the latest OS for maximum security, or does that not matter?
Are there any other things you can think of that would make this system less secure than the server based model? (I can think of at least one: If you had a bug in an old version of the app which printed the entire contents of a table, for example, there would be no easy way to correct this. A user could purposely avoid upgrading their app with the intent of stealing people's private data forever. The only way around this would be to migrate all the data to new tables that the old client doesn't know about. I'm sure this would be a pain.)

Node.js user system

I'm currently working on a web application which deals with multiple users. Whilst it currently works, it relies on some real bad practises which I'll outline in a minute.
We're using MySQL as the database system, since we're updating our current application, we want to ensure everything is backwards compatible. Otherwise I'd look at MongoDB etc.
Our users are stored in a table aptly named login. This contains their username, email, hashed password etc and a field which contains a JSON encoded object of their preferences. There is no real reason for doing this over using a meta table.
So the bad practises:
We're storing the entire users login row, excluding their password (although this is an internal-only app) in a cookie. It's JSON encoded.
Once the user logs in we have a secure HTTP cookie, readable only via Node.js for their username and their password so that we can continue to keep the user logged in automatically.
We have a app.get('*') route which constantly ensures that the user has their three cookies and updates their acc cookie with new preferences. This means that every time the user switches page or accesses a new AJAX item (all under the same routes) they have an updated cookie.
Every time a user performs an action we do this to get their user id: JSON.parse(res.cookies.acc).agent_id yuck!
Now, each user is able to perform actions to certain elements on the page, this effects everyone as the application is internal and anybody can work on the data inside of it.
I know what I want to achieve and how it should be done in say PHP, but I can't figure out the most effective way in Node.js.
I've started creating a User module which would allow us to get the user who performed the action and neatly update their preferences etc. You can see this here bearing in mind that it's a WIP. The issue I'm having with the module is that it doesn't have access to the users cookies, since it's not "a part of" Express. Which explains the last bad practise.
What would be the best way to handle such a system and remain bad-practise free?
I doubt it meets all of your requirements but its worth checking out out Drywall; A website and user system for Node.js
Hopefully it (or parts of it) could be helpful to you.
http://jedireza.github.io/drywall/

User specific version of extensions from Chrome Web Store

I've been developing and maintaining a Chrome extension for my company where each customer would be assigned a unique ID in the code. We've been using the ID to determine license status and login to our services (paid extension with monthly subscription fee).
So far we've hosted the extension files ourselves and had unique update URLs for each customer extension. This has been nice and simple; go to our website, click install and you're done. With the latest Chrome release, however, that installation procedure has been thwarted by Google since they now require users to install extensions by dragging and dropping the CRX files into the chrome://chrome/extensions/ tab. Unless of course your extension is available through Chrome Web Store - which leads me to the problem:
We don't want the drag and drop CRX installation - requires Web Store.
We don't want multiple versions of the extension (one for each customer) on the Web Store since that's a maintenance hell every time we update the extension.
We don't want to use Web Store licensing because:
It requires OpenID login.
We sell the extension to schools with many students where the school pays the bill - not the student.
We don't want to lock our payment method to one browser, i.e. we want to be able to maintain licensing and payment through our or servers.
We don't want to have users input a license key since that's too much of a risk with several thousand students having to input the key - also it requires some kind of storage (cookies/localStorage) which would eventually get cleared requiring the license key to be input again.
I'm not 100% certain that my statements are completely correct, so feel free to enlighten me if I missed something.
If they are, the question is whether or not we can somehow tailor the extension for each customer through the Web Store (using the unique ID) without needing to publish one extension per ID?
As a side question any answers that might solve the problem with another method will also be accepted.
For the answer below, I assume your app is a packaged app, not a hosted app.
I have a solution that's fairly similar to your current implementation, but adds one extra step for users. For the student user, the process will work like this:
Download the app from the Web Store. The app does not function yet, and launching it just displays a "Please click the activation link provided by your school/institution" message.
Click a link hosted on your server (i.e., the server where you used to host the update URL) that looks like https://myserver.com/activateapp.php?custid=123456789. You host one such link for each institution you support, and it is the institution's job to provide its link to its students. This link activates the app.
From an implementation point of view, here's how it works:
Host a page, https://myserver.com/activateapp.php, on your server. Server-side, check that the custid parameter is valid. If it is not, send a 404 error.
Your app has a content script that is injected into https://myserver.com/activateapp.php that scans the URL and picks out the customer ID. Once the app finds the ID, it stores it in localStorage. Since invalid customer IDs produce a 404 error, you know that when the content script runs, the page is not a 404 error; therefore, it is reading a valid customer ID.
Any time the app wants to query your services, it checks if it has a customer ID in localStorage. If it does, it uses that ID; if it does not, it displays a message that the app has not been activated yet. Packaged apps will never have their localStorage erased unless your app is programmed to wipe its own storage, or the user does it from the console. Storage erasure will never "accidentally" happen. Even the strongest browser-wide data/cache purge will only clear localStorage from Web pages, not from apps and extensions.
For extra security -- if you don't want people randomly guessing customer IDs -- you can add an extra signature parameter, like https://myserver.com/activateapp.php?custid=123456789&sig=2464509243. This extra parameter is some server-verified transformation of the customer ID (ideally a cryptographic signature or a purely random value associated with the ID in a database) that is impossible for anyone to guess. When the request for activateapp.php hits the server, it checks for a valid customer ID and a valid corresponding signature. Of course, this doesn't stop people who have legitimate access to a valid link from sharing the link to unauthorized people, but I expect that was a vulnerability that existed in your old system anyway.

Resources