AppGov Score Blog

Check out our latest updates!

How to Restrict Microsoft Graph API Access to Mailboxes

June 12, 2024 Louis Mastelinck

Laptop with email restriction icons

The Microsoft Graph API offers developers and IT professionals a platform, providing access to an extensive array of Microsoft 365 services and data through one unified endpoint. It allows us to communicate with Microsoft 365 (which includes OneDrive for Business, Outlook, Microsoft Teams, and more), Entra ID (formerly Azure Active Directory), Dynamics 365, and additional services, allowing for smooth integration and interaction with user information, organizational data, and resources within the Microsoft ecosystem.

For example, if an adversary gains access to app registration credentials with permissions to alter settings, navigate through content like mailboxes and user profiles, or even send emails, it could lead to significant security breaches.

Widely Used by IT Admins

IT administrators frequently use the Graph API in their scripts, Logic Apps, Power Apps, and other automation tools like backup solutions and reporting tools. This integration streamlines various administrative tasks and processes, making it a valuable tool in the IT toolkit. The Microsoft Graph API is the most suggested way to perform actions within your Microsoft 365 tenant programmatically. It provides a unified endpoint (https://graph.microsoft.com) to access various Microsoft services and data, enabling developers to integrate and manage resources like users, groups, mail, calendars, contacts, files, and more.

Application permissions via App Registrations

To interact with the Microsoft Graph API, applications must be registered in Entra ID. When you go through the process of creating an app registration with the necessary Graph API permissions, you encounter two types of application permissions:

Delegated permissions

These permissions are used by apps that have a signed-in user present. They allow the app to act on behalf of the user and access resources based on the user's permissions. Examples include reading the signed-in user's profile or accessing their mailbox.

Application permissions

These permissions are used by apps that run without a signed-in user. They allow the app to access resources directly and are suitable for background services, websites, scripts, automation, etc. Examples of potential actions include accessing all users' profiles or emails within an organization or sending emails on behalf of any user. A real-life scenario highlighting these risks is the Microsoft breach performed by Midnight Blizzard.

Common use case: sending email messages

A common use case for the Graph API is sending emails from an application. By setting up a connection with the Graph API, applications can programmatically send emails, which is particularly useful for automating notifications, alerts, and communication within an organization.

Application permissions: sending email messages

As IT administrators or software vendors, we should always strive to follow the principle of least privilege. Suppose we examine the permission set of the Microsoft Graph API and look at the application permission Mail.Send, we can see that the description of this permission states, "Send mail as any user."

 

graph-api-1
Figure 1:  API permissions for an app registration in the Entra portal

 

In this case, having permission to send emails as any user in my tenant is excessive. Imagine if this app gets compromised and adversaries can impersonate and send emails from our tenant. This could lead to potential misuse of our email system.

Unfortunately, in the Entra portal, you are not able to define any limitations on which emails it should solely send from.

How to Limit the Permissions Scope

Using the following steps, you can identify which apps use the Graph API to send emails and how to restrict them to send only from the email addresses to which you give them permission.

Get an overview of permissions granted to application registrations

The Entra portal doesn’t provide an easy way to get an overview of all app registrations and their permissions; you must manually verify each app by going to the API permissions overview. There are methods that surface this information in a more structured way:

Using ENow App Governance Accelerator
In ENow’s App Governance Accelerator, the All API Permissions report provides a straightforward way to get a table with all API permissions that application registrations in your tenant have. This report is not limited to the Graph API; it reports on all APIs for which application registrations have permission.

The report allows you to filter on the Mail.Send Graph API permission, for an overview of all application registrations with this permission.

Using Powershell
Alternatively, you can utilize the following PowerShell script to write a list of applications and their Graph API permissions:

 

# Step 1: Install the required Microsoft Graph PowerShell Modules
# This only needs to be done once

PostInstall-Module -Name Microsoft.Graph.Applications -Scope CurrentUser
Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser

#Import the needed modules
Import-Module Microsoft.Graph.Applications
Import-Module Microsoft.Graph.Authentication

# Step 2: Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.Read.All"

# Step 3: Fetch all app registrations
$appRegistrations = Get-MgApplication

# Step 4: List permissions for each app registration
foreach ($app in $appRegistrations) {
   Write-Output "App Name: $($app.DisplayName)"
   Write-Output "App ID: $($app.AppId)"

   # List required API permissions
   Write-Output "Assigned API Permissions:"
   foreach ($requiredResourceAccess in $app.RequiredResourceAccess) {
       foreach ($resourceAccess in $requiredResourceAccess.ResourceAccess) {
           $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$($requiredResourceAccess.ResourceAppId)'"
           $permission = $servicePrincipal.AppRoles + $servicePrincipal.Oauth2PermissionScopes | Where-Object { $_.Id -eq $resourceAccess.Id }

            if ($permission) {
               Write-Output "  - $($permission.Value) ($($permission.Type))"
           } else {
               Write-Output "  - Unknown Permission ($($resourceAccess.Type))"
           }
       }
   }
   Write-Output ""  # New line for readability
}
# Disconnect from Microsoft Graph
Disconnect-MgGraph

 
Get an overview of application registration activity using Graph API Activity Logs

Another way to identify app registrations and the use of their permissions is through the Microsoft Graph Activity Logs. The Graph API's logging is not activated by default but can be easily enabled. To do this, you will want to send your logs to an Azure Log Analytics workspace so we can query them for investigation.

Steps to Enable Graph API Activity Logs

  1. Enable Logging:
    1. Go to Entra > Monitoring & Health > Diagnostic Settings.
    2. If you don’t have a diagnostic setting already, create a new one. If you do have one, verify that the "MicrosoftGraphActivityLogs" checkbox is enabled.
    3. Choose the Send to Log Analytics workspace option, as shown in the below screenshot:

 

graph-api-2
Figure 2: Enabling Microsoft Graph Activity logs in Entra ID

 

Ensure your logs are sent to a Log Analytics workspace for querying and analysis.

Enabling this logging allows you to verify which app registrations have permissions, provided they have shown activity within the time frame defined in your log search query. This means that using this method, you will be able to map the app registrations that are currently in use and have generated logs in your table.

KQL (Kusto Query Language) is a query language used to retrieve and analyze large datasets stored in Azure Data Explorer and other Microsoft services like Azure Monitor and Microsoft Sentinel. We are going to use KQL to analyze the activity of the Graph API, allowing us to filter, sort, and transform data for detailed insights.

KQL: Find app registrations with Mail.Send role
Use this KQL query to see if in the last 30 days an app registration was active that had the Mail.Send permission:

MicrosoftGraphActivityLogs 
| where TimeGenerated >= ago(30d) 
| where Roles has "Mail.Send" 
| distinct AppId, Roles 
| project  AppId, Role

 

graph-api-3
Figure 3: the result of KQL query searching for app registrations that have the Mail.Send permission

 

The logs do not include a translation of the AppId attribute to the display name of the app registration. So, you will have to look it up in Entra ID to identify which app registration it is.

Find the email senders
If we analyze the Graph API call below, you'll see that it sends a POST request containing the string "sendMail."

 

graph-api-4
Figure 4: An example of an API call that sends an email via the Graph API

 

See that the request is sent to the API endpoint /sendMail. This is something we can work with.

When you start looking through the logs, we notice that the RequestUri to send email from another mailbox contains the email address.

 

graph-api-5
Figure 5: Investigating RequestUri in the logs

 

Building a KQL Query
The next step is building a query that gives us a nice overview of all email addresses that are found in a send email RequestUri:

KQL: Find email addresses being send from
MicrosoftGraphActivityLogs 
| where TimeGenerated >= ago(30d) 
| where Roles has "Mail.Send" 
| where RequestUri contains "/sendMail" 
| extend EmailSendFrom = split(RequestUri, "/")[-2] 
| project AppId, EmailSendFrom, TimeGenerated

 

graph-api-6
Figure 6: KQL query and result set

 

Now that we have identified the app registration ID and the email addresses they utilize, we can commence restricting their access to these specific email addresses.

Application access policy

Exchange Online allows us to create application access policies that restrict app registration access to predefined mailboxes. To achieve this, we need to create a new mail-enabled group. In that mail-enabled security group, we will add the email address from which our application will send emails. Note that you are required to set an owner for the mail-enabled security group. I use an account that has no mailbox configured.

 

graph-api-7

 

Now, use PowerShell to connect to Exchange Online, using the following commands, and create an Application Access policy.

Connect-ExchangeOnline -UserPrincipalName <user principal name

New-ApplicationAccessPolicy -AccessRight RestrictAccess -AppId "fillin app id" -PolicyScopeGroupId "**exampleGraphApiMailerApp@mastelinck.onmicrosoft.com**" -Description "This policy makes sure that my app registration can only send via these email addresses assigned in the mail enabled security group"

The screenshot below shows an example:

 

graph-api-8
Figure 7: Example PowerShell command creating application policy

 

Next, you can verify the result of your policy using this PowerShell command:

Test-ApplicationAccessPolicy -Identity <emailadress> -AppId <appid>

 

graph-api-9
Figure 8: Example PowerShell command verifying that access is denied

 

graph-api-10
Figure 9: Example PS command verifying that access is granted

 

You can see that the accessCheckResult value provides the result if your app registration has access to the mailbox.

Conclusion

There are major risks associated with application permissions that have broad access across all users in your tenant.

With the goal of achieving the principle of least privilege, we began by identifying which applications have permission to send emails. Using the Graph API Activity Logs, we identified active app registrations with "Mail.Send" permissions and the corresponding email addresses they use to send email messages.

Next, we implemented an application access policy to ensure that these app registrations only have access to the mailboxes they truly need.

Implementing these steps will help you achieve the least privilege principle that we all strive so hard to attain and uphold.

 

Do you know if your SaaS applications and app registration permissions within Entra ID are creating a security risk? The ENow AppGov Score is a free security assessment tool that will quantify your application security and governance state quickly.  In addition to providing your AppGov Score, the tool will provide a comprehensive Application Governance Assessment report that includes each test, your result, and why the test matters. Sign up to get your score and assessment report in just a few minutes - Get Your AppGov Score today!

Share This:

Louis Mastelinck

Written by Louis Mastelinck

Louis Mastelinck is a Belgian Soc analyst and security consultant with a passion for keeping the digital world secure. Specializing in incident response and the Microsoft Security stack (MDE, MDO, MDI, MDCA, Sentinel, ...), he excels at neutralizing threats and protecting organizations. As a Microsoft MVP and GCFA-certified professional, Louis brings a wealth of expertise to the table.