I am trying to understand few points about delegating authorization to Azure BLOB using Azure service principles.
How I configure Azure:
Create and Configure Service Principal: in Active Directory I created an application, created a key (password), and set its required permissions to access Azure Storage;
Configure IAM of Azure Storage: under Storage accounts section, I chose my storage account, and under IAM I assigned my account (my login account as XYZ#hotmail.com) to Storage BLOB Data Contributor role.
How I use the configuration in my client App:
With the aforementioned configuration; my application takes tenant ID, client ID, client secret, and etc. and sends authorization request to /authorize endpoint.
Then a window pops-up and asks me to login (using e.g., XYZ#hotmail.com), and then a consent screen pops-up and asks for my permission to allow the service principal to read my Azure storage.
After I approve, my client application receives OAuth2.0 code.
Then I exchange this code with an access_key via the /token endpoint.
Q1: does this access_key grant my client application same privileges as XYZ#hotmail.com or service principal?
Using the obtained access key I can read/write azure blob.
Q2: if XYZ#hotmail.com does not have read/write access (i.e., not assigned with the Storage BLOB Data Contributor role), my client app would not be able to read/write for the blob, regardless of the Service Principal's role. This is where I get confused, I am under the impression that my client App is assuming Service Principal, hence it will have same privileges as the Service Principal, not XYZ#hotmail.com. For instance, XYZ#hotmail.com can have Contributor role (i.e., read/write), while the service principle would be assigned with a Reader role. In that case, I would have a full access to BLOB storage, while my client app will have only a read access to the BLOB storage. However, it seems client app gets same permissions as XYZ#hotmail.com. What am I missing here?
Q1: does this access_key grant my client application same privileges
as XYZ#hotmail.com or service principal?
The access_token you got has the delegated permissions which onbehalf of XYZ#hotmail.com. This is because that you used Authorization code grant flow.
What am I missing here?
For your scenario, I suggest you can use client_credentials flow for your app.
With client_credentials flow, you will get access token only with the application permissions configured in AAD. Also, this access token is actually onbehalf of your AAD application itself. If you assign a role to your service principal, you will get the access token with the permission of that role.
If you use Authorization code grant flow, you will get the access token onbehalf of the user, not the service principal. So, the access token will have the permission of that user.
Related
We had a user's creds exposed and a threat actor used them to successfully log in to Azure CLI with the user's creds.
We've since resolved the access issue using conditional access and our MFA (which admittedly was a hole).
I'm trying to recreate the method of attack though and I can't seem to get it right.
Here is the activity details for the malicious sign-in:
Application
Microsoft Azure CLI
Application ID
04b07795-8ddb-461a-bbee-02f9e1bf7b46
Resource
Windows Azure Service Management API
Resource ID
797f4846-ba00-4fd7-ba43-dac1f8f63013
Resource tenant ID
LEft out
Home tenant ID
Left out
Home tenant name
Client app
Mobile Apps and Desktop clients
Client credential type
None
Service principal ID
Service principal name
Resource service principal ID
d2b4c9e3-9a2a-4360-8ba4-6ece086335c5
Unique token identifier
Left Out
Token issuer type
Azure AD
Token issuer name
Incoming token type
None
Authentication Protocol
ROPC
Latency
90ms
Flagged for review
No
User agent
Looks like they used ROPC detailed here https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
I've tried emulating it through Azure CLI directly but it doesn't report back "ROPC" as authentication. So they are definitely calling through ROPC.
Then I tried emulating it with my creds in Postman and I get almost the same result as above in the sign-in log:
Application
Microsoft Azure CLI
Application ID
04b07795-8ddb-461a-bbee-02f9e1bf7b46
Resource
Microsoft Graph
Resource ID
00000003-0000-0000-c000-000000000000
Resource tenant ID
Left out
Home tenant ID
Left out
Home tenant name
Client app
Mobile Apps and Desktop clients
Client credential type
None
Service principal ID
Service principal name
Resource service principal ID
e10569b0-24e4-4495-9d9b-698b01290eae
Unique token identifier
Left out
Token issuer type
Azure AD
Token issuer name
Incoming token type
None
Authentication Protocol
ROPC
Latency
108ms
Flagged for review
No
User agent
PostmanRuntime/7.30.0
As you can see it's very similar, but mine is reporting "Microsoft Graph" while the malicious entry reports Windows Azure Service Management API.
Can someone point me in the right direction?
Windows Azure Service Management API refers to the Azure Resource management API.
I tried checking Sign in Logs and the Service Principal Sign In’ Logs has Windows Azure Service Management API refer here :-
Note- The above sign in log is of the Service principal sign in with
Client credentials flow. You can find that Service principal by
copying its Application ID and pasting it in app registrations page or
enterprise application page of Azure AD.
I tried to log in to Azure with service principal named Powershell with ROPC Flow via Postman
Received Access token like below :-
Called Graph API
Got resource as Microsoft Graph in Sign in Logs similar to you:-
Now, I tried calling Azure Resource management API to get list of Azure resources from my account with the same Flow and got the Resource set to Windows Azure Service Management API like below :-
Added Azure Service Management API permissions:
Now, I changed the scope to https://management.azure.com/default like below:
Fetch the access token from above call and ran below query to get list of resources:
When I checked sign in logs now, it’s showing ROPC with Windows Azure Service Management API resource like below:
I created Azure service bus and clients are able to access the service bus using Shared Access Token. All this is working fine and now we want to implement authentication using Azure AD.
This is what we know so far:
Using Azure AD we will register client(s).
Each registered client will get an ID and Secret.
We will use this ID, Secret and AppId (Id of service bus) to get access token from Azure AD
This call will return an access token.
We then pass the access token to Azure Service Bus using request headers to post or get messages.
My question is how does Azure service bus actually validates this token ? I have seen another example where API or Service which is consumed by the client, validates the access token using a program like this: https://github.com/mauliksoni/aad-token-validation/blob/main/DotNetFramewrork/validate.cs & then only user is allowed to access the token.
Ideally Service should validate the access token on its own and there should not be any need to validate tokens by externals programs.
You don't need to do any external validation, as long as the service principal got the right RBAC role to perform the desired operation(send/receive/management), the service will do the remaining token validation for you.
I am trying to find security best practice on App permissions in the context of azure resource management.
Currently, there is only one permission listed for management.azure.com and it is
management.azure.com/user_impersonation (preview). This delegated user impersonation can be a serious problem and it can led to account takeover by malicious app.
Think about a scenario where a user with global administrator role consent and authorize an access token to the app. App can use the token and do whatever it wants with the azure tenant.
Another scenario where a privileged user assigned contributor role to multiple subscriptions. Token authorized by this user can be misused by app to modify resources in any of the subscriptions.
Unlike graph (graph.microsoft.com) api where you can handpick the permission (user.read), resource management api has only one option - user_impersonation!
You may argue why would a privileged user authorize the action but people make mistakes. Our job is to stop or minimize such risk by design. So, what's the best way to allow app to manage resources in azure and minimize the security risk?
Thanks to #juunas for outline and tips. Thanks to #Gaurav for attempting to address my question. I was able to modify azure resources on a subscription without having to grant user_impersonation on management.azure.com api. Here are the steps-
1) Register an app (TestPermissions in my case)
2) Add API Permissions (optional). You don't need to add management.azure.com.
3) Go the Azure resource (subscription, resource group or management group level based on your requirement) and add IAM/RBAC role to the registered app. I assigned Contributor role to TestPermissions app at the subscription level.
4) Request a oauth2 access token following client credential grant flow. You can provide client_id and client_secret in the body of the POST request or you can provide it as Authorization Basic base64 encoded header (that's what I did). Save the access token for future use (until it expires).
Note: I could not add multiple audience (scope) at the same time. If you would like to get a token for graph api, you can request a separate token by changing the scope to http://graph.microsoft.com/.default
5) Use the access token captured in the previous step to interact with azure resource manager. You will need to add the jwt bearer token in the Authorization header (not shown here) on every request to https://management.azure.com. In this example, I am creating a new resource group named TestCreateRG003 to one of my Pay-as-you-go subscription.
6) Let's validate/verify that the resource is created or updated in Azure. Bingo, there they are! App can read/modify (based on RBAC) azure resources w/o having to grant impersonation permission.
It is true that by granting that permission you are allowing the app to act as you, with all the permissions that brings.
The main way I've seen used when limitations are desired is that you:
Register an app in your Azure AD
Grant the service principal the necessary roles (e.g. Reader on specific resources)
Set the tenant id, client id, client secret etc. in the app
This of course requires that the app itself supports this approach.
If it only allows usage through impersonation, then you'll need to either trust or not use it.
Let me see if I can answer this question.
When a user requests a token for management.azure.com, all is done at that time is that the user has permission to execute Azure ARM API. That doesn't mean that they can do everything that's possible with Azure ARM API.
The things that they can do is controlled by Azure Role Based Access Control (RBAC). So if a user is in the Reader role, the token got on behalf of the user can only read information about resources in their Azure Subscription. They will not be allowed to create, update or delete resources in their Azure Subscription.
What you will need to do is grant users appropriate RBAC role to minimize the risks of misuse.
All of the examples on the microsoft azure site having to do with a client application or a service principal having access to azure apis all show the graph apis as the example.
I got those to work, but what I need to do is access the azure service management apis.
If I take an auth token that works for the graph api and use it for the service management api, I get an "Authentication failed." error.
I have added the api permission for "azure service management" and the user_impersonation delegated permission type to my public client app, but the management apis still fail.
Am I going about this all wrong?
I get the idea that the service management apis are authenticated a different way since there's absolutely no mention of using them anywhere in any of the auth examples.
I suppose you are using the client application or a service principal to access azure resources. If so, you are using the client credential flow, but the user_impersonation of Azure Service Management API is a delegated permission, it will not take effect in this flow.
The permission is used to be consent by the user who login to the AD App e.g. in the auth code flow, when it has been consent by the user, the App will be able to represent the user to access the azure resources, note the permission is from the user, not the app. So if you want to use this flow, the user should have a role in the subscription or specific resources.
To access the azure resources with the client application or a service principal, you just need to add the service principal as an RBAC role in the Access control (IAM) of your subscription or specific resource, no need to add any API permission in Azure AD, see this link. Then the service principal will be able to access the azure resources.
When you getting the token to access the azure resources, e.g. call the REST APIs, the scope should be https://management.azure.com/.default, not the https://graph.microsoft.com/.default, you can just get the token for one resource in one time.
I am working with Azure AD through OAuth 2.0 protocol and also creating a Service/ Dameon application to handle the authentication process for Microsoft Graph SDK. For the service/daemon, I make a HttpWebRequest and pass along the client_id and client_secret to generate an access_token where I then can supply to the Microsoft Graph SDK.
I also have successfully created a corresponding service principal to the target tenant, in which an admin has granted permissions to the application using the authorization code grant flow. The application then shows in Overview -> Quick tasks -> Find an enterprise app, within the (portal.azure.com).
My question is there an approach where I can leverage the service/daemon approach while also allowing an admin from the target tenant to authorize the application, that would allow the target tenant to create a client_secret to pass which would be unique to that tenant?
Short answer is no. When an admin consents your multi-tenant app:
A Service Principal is created for it in their tenant
Permissions requested by the app are granted in that tenant
This means your app can now authenticate with its client credentials (id + secret) against their tenant as well. So the same keys work in all approved tenants.
What that means is your app is free to get an access token for any of them at any given time, no matter who is signed in. So it puts some responsibility to your app to keep data separated.
If you get an access token from https://login.microsoftonline.com/company.com/oauth2/token, the resulting token will contain that tenant's identifier. And APIs like the Microsoft Graph API will only give you data for that tenant with that token. So your app must make sure to only use a token that has a tenant id equal to the user's tenant id claim.
I would say that juunas' answer is 99% correct. The short answer is basically no, and the considerations he mentions are also solid.
But I believe that this would be technically possible under certain considerations. When the admin consents to your daemon service, a service principal is created in your customer's tenant. Service principals do allow addition of credentials that can be used as client secrets on a per-tenant basis. The thing is, there's not really a way to add a credential to a service principal programmatically from your app. You would have to get an administrator to run some script to add the new credential to their tenant's service principal.
Even if you went through all this, you would need to make sure that your service is also isolated on a customer/tenant basis. Security-wise, it's sort of pointless to create per-tenant client secrets if your singular daemon has access to all of the secrets.