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.
Related
I want to create an Azure Function that accesses Cosmos DB Containers and Key Vault Scopes. Function calls require Active Directory sign in and users are granted access to resources via Groups.
Azure resources should be accessed or denied based on the signed in user's permissions or group memberships.
How can the Azure Function access other Azure Resources on behalf of the authenticated caller?
It's pretty complex unfortunately, you'd need to have a new OAuth2 access token scoped to cosmosdb resource to be able to access it.
Please check OAuth2 On-Behalf-Of flow:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
When I used it 2 years ago, there was no SDK support for this flow, I simply used http request against https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token
In your case, you would also have to add https://cosmos.azure.com/user_impersonation delegated API permission to your app registration and users will be asked for a consent when they try to access your API.
I have created an API that is protected by OAuth using an app registration in Azure.
My app registration does not require assignment, but it exposes a number of roles that the underlying API verifies. To my understanding, this accomplishes almost the same thing as requiring approval.
So far I've only had user/group roles but now I've added an application role intended for integrators, and I want other application owners to be able to request permission to my API. I, as the API owner, would like to review these and either reject or consent to the request. E.g. I don't want everyone to be able to access my API within the tenant without my knowledge, just like all users/groups don't have access with me assigning them to a role.
The Role-based access control for application developers documentation makes it very clear who manages access:
...an application developer defines roles rather than authorizing individual users or groups. An administrator can then assign roles to different users and groups to control who has access to content and functionality.
However, if you create a role with allowed member types set to application, things are not quite as clear and it seems to behave more like a scope, where I give up any access management. Also from my limited understanding, a scope is used when the API needs to request data from the user (e.g. wanting to read their username), whereas a role is used for the application developer to control access to what they are developing.
This is what it looks like when I request access to my API from another app:
This same page mentions the following information:
The "Admin consent required" column shows the default value for an organization. However, user consent can be customized per permission, user, or app. This column may not reflect the value in your organization, or in organizations where this app will be used.
As well as:
Applications are authorized to call APIs when they are granted permissions by users/admins as part of the consent process
However, from my reading, it sounds like this never gives me, as the API owner, any insight into who has access to the API I own. I want to control application access the same way I'd assign a group or user to a role in the enterprise application.
Can this be achieved when it's an application on the other end, not a user? If not, how would I allow applications to integrate in a controlled manner?
I want to explain the feature Azure ad provided to protect web api here.
As you know, we usually use a token in the request header to let the api check if the request had correct permission to visit the api. Such as if the request from an allowed user role, right? So to whole progress should be authentication and authorization. Users sign in first then try to generate an access token to visit an api. Azure AD has similar architecture.
If you had a web application(e.g. web mvc app) you can integrate Azure AD into it then you can allow users use their user1#xx.onmicrosoft.com account to sign in. If you had a web api project, you can also integrate Azure ad and add [Authorize] attribute above the controller so that the incoming request should contain a correct Bearer token which we call it access token.
For Azure AD, we usually have 2 options, verification scopes or app roles. That results from the different flows we used to generate the access token. For example, we use auth code flow to sign in users and generate access token containing scp claim which is granted delegated api permissions. And we use client credential flow to let an application to generate access token containg roles claim which representing it's granted application api permissions. In short, when we set [Authorize] + [RequiredScope(scopeRequiredByApi)] in the controller, it allows requests from a user(user sign in the app and call api), when we set [Authorize(Roles = "roleRequiredByApi")], it allows requests from the application(no user signed in and the app call api by itself).
Here scopeRequiredByApi and roleRequiredByApi is what you exposed and then added to App Registration > Permissions. Just like Integrator you marked in the screenshot, it can be recognized as roleRequiredByApi because its type is Application.
And I'm afraid the roles is not what you want but to be honest what I said is what AAD can do for you... And I think the document I mentioned above about verification scopes or app roles will be a good sample for you.
The App Registration > Permissions section has a great feature for reviewing and limiting the access provided for your app registration:
enter link description here
In addition you should always define the scope of your permissions and limit it to the least required for your app. eg. NEVER set scope at the subscription level! Always set it at the resource group or lower.
Also note that Microsoft now provides Defender for APIs and you can use Sentinel to monitor a lot of the activities related to your app registration. Always always enable logging wherever possible and configure some method of alerting/reporting so you can better understand the activities for your app.
General Overview of what I am trying to achieve
I am trying to build an Azure AD Multitenant Web application which allows me to manage resources in customer subscriptions/tenants using the Azure Resource Manager (ARM) APIs. I am pretty new to Azure AD Multitenancy.
The Ideal control flow
1. A customer browses the Applications (ideal an admin of the customer tenant)
2. Will be granted with Azure AD authorize flow
3. Accepts everything and grants admin consent for the AD App in their tenant
4. Unclear: The AD App will be granted contributer access on a subscription or resource
5. My Web App will be able to use the AD App credentials to manage the customer resources using the ARM APIs
Problem
Steps 1-3, 5: Are clear and I know how to build that.
Step 4: I am not sure how to build that so that step happens automatically.
Solutions I have considered
The worst case would be the customer AD Admin must manually grant the AD App access to a subscription or resource using the Azure Portal.
One idea that came to my mind is that you require the user_impersonation permission on Azure Service Management API.
After the user logs in, you could list out the subscriptions available, allowing the user to select one.
Then list out the resource groups if needed.
Once the user confirms a selection, your app could add itself as a Contributor on the targeted resource through the Management API, on behalf of the currently signed-in user.
To do this, you will need the object id of the service principal for your app created in the target tenant.
You can get it by acquiring an app-only token for e.g. the Azure Management API from that tenant's token endpoint after the user has logged in.
The token will contain an oid claim, which is the object id for the service principal.
Of course the user who signs in would have to have the ability to modify access to the target resource.
I would say the downside of this approach is that the organization must trust your app to only do the thing it claims to do.
The approach where they grant the access manually allows them to be in control fully.
I am trying to call Azure Maps with OAuth access tokens but it is throwing me 403 Forbidden with the message "Permission, capacity, or authentication issues.". I have followed procedure mentioned here: https://learn.microsoft.com/en-us/azure/azure-maps/azure-maps-authentication
Created App Registration in AD, generated secret
Added API permission to Azure Maps
In Azure Maps > IAM > added the application as Map Data Reader
Got the Access token from https://login.microsoftonline.com//oauth2/token with resource=https://atlas.microsoft.com/
Calling https://atlas.microsoft.com/route/directions/json?api-version=1.0&query=52.50931,13.42936:52.50274,13.43872 with x-ms-client-id and Authorization=Bearer
Same procedure works correctly for my personal free subscription but not in my company's subscription. Don't know how to debug.
For reference on how Azure RBAC works.
Ensure you have no deny assignments possibly enforced from management groups on the particular role.
Make sure the role assignment is applied to the correct scope. Meaning on the scope of an Azure Maps Account or a parent of the account such as the resource group or subscription.
If you have security principals assigned to the correct scope but still receiving 403. It usually means you have assigned or authenticating with the wrong security principal.
Example:
"App only" token for an App Registration requires the service principal App to be assigned at the scope.
If you as user authenticate as a user to the App Registration that would mean the user security principal should be assigned to the scope; Not the app.
If you are using Azure AD groups it could mean that the security principal may not be part of the group which is assigned access.
I don't think it's common to add service principals to security groups though. But it is a possibility which should be confirmed. There is also a possibility of delay before the permissions are propagated but this usually shouldn't take more than a few minutes.
Just to be thorough but may not apply here. Certain REST APIs require S1 sku to be selected on the account. This will result in the same error response.
We raised the ticket with Microsoft and they told us it was caused by:
longer synchronization times regarding the RBAC configurations
worldwide
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.