I have a web app which authenticates the user using an external identity provider (Microsoft Entra with MSAL library). This give us an access token to access our API.
After authentication (so, we already have the token), the user must select the company he wants to work with. The "company list" shows only those for which the user has permissions.
We would like to have the CompanyId in the token, but it is not known for the Identity Server at the moment of login (user interaction is needed to pick a company and Companies are a table in our DB, not in Active Directory).
After our user picks a Company, the CompanyId is added (as a custom header) to all http requests.
At this point, we are concerned about the security implicances. A malicious user could change the CompanyId (even if it is a hash) and access other's company data.
Is it a legit concern? What could we do to mitigate this risk?
We have considered:
- Issue a second token (after Company pick) and include on it the CompanyId. Unfortunately, MSAL does not allow to build a token with data obtained outside of MSAL (as far as we know).
- Check if the user has access to the company in every query to the DB.
- Cypher the CompanyId in such a way that would not be useful to copy and paste for future use.
What would you recommend?
-
I think you can add custom claims to the token : learn.microsoft.com/en-us/entra/identity-platform/…Ewan– Ewan2024年02月03日 15:38:58 +00:00Commented Feb 3, 2024 at 15:38
-
Thanks Ewan. Yes, we have tried that way, unfortunately Active Directory needs to know the CompanyId to generate the token. In our case, the user needs to pick this Id after login. Moreover, the Active Directory doesn't have any table with CompanyIds to pick from. Yes, we could assign a set of CompanyIds to each user (in a custom claim), but we will not know which one has been picked during authentication... unless somehow we add a kind of Select-box during login, but in that case, Active Directory would need to know the Company DB contents. Probably we are missing something tho.zameb– zameb2024年02月03日 18:08:44 +00:00Commented Feb 3, 2024 at 18:08
-
if the user selects the company when they sign in, then you have no security hjowever you cut it. I assumed the user is selecting from an allowed list? you could put the allowed list in the token and validate the selected company against itEwan– Ewan2024年02月03日 21:56:38 +00:00Commented Feb 3, 2024 at 21:56
1 Answer 1
Is it a legit concern?
Yes.
The Company-Id:
header field you are describing is
what's known as "attacker controlled data",
so you should not blindly trust it.
After our user picks a Company, the CompanyId is added (as a custom header) to all http requests.
This makes it sound like User is running your JS in his browser, where he could e.g. modify the running code or interpose a MitM proxy. So our User controls the content of all web headers sent from client side.
Well, it's simple.
Roll a secret random GUID, and call it key
.
It stays strictly on your server(s), and is
never revealed to clients.
Feel free to do routine key aging by re-rolling a new one every N weeks.
Think of the MSAL-issued token as being immutable.
Look, but don't touch.
It contains a username.
The form of the token visible to your server will be called token
.
Server validates token
each time it reads it,
throwing fatal exception if it sees a tampered-with invalid one.
Upon obtaining validated token
server prompts User to choose
from his authorized set of companies, e.g. "Ace Chemicals" or "ACME".
User is not authorized for "Ajax", a company an attacker would like to access.
Subsequent requests might include these headers, where 2nd one is an attack:
Company-Id: ACME
Company-Id: Ajax
As part of the prompting flow, server computes and sends a prefix of this validator:
SHA3(key, username, "ACME")
so we wind up with a header that looks like this:
Company-Id: ACME;3d8505637f64bb3c
(I am depicting hex nybbles, but you might prefer more efficient base64 encoding.)
The point is that server produces a validator just once, when the
external identity provider has convinced it that User may legitimately access ACME.
Thereafter, when reading the Company-Id:
header,
server will always re-compute the validator hash,
and throw fatal exception upon mismatch.
A company ID that survives this process may be trusted by server code.
-
Wonderful, this is a perfect match for our situation. Thanks!zameb– zameb2024年02月03日 18:01:48 +00:00Commented Feb 3, 2024 at 18:01