Introduction
Microsoft Graph is a unified REST API endpoint that allows you to query data from various sources, including data stored in Microsoft 365. Microsoft Graph provides access to a suite of services including Microsoft 365, Enterprise Mobility + Security, and Windows. In the wrong hands, access to Microsoft Graph API can be misused by threat actors to compromise users; for example, by reading emails or files on SharePoint.
Responding to and detecting these cloud-based attacks is of utmost priority. Multiple products and logs are available to help with threat investigation and detection. In this blog, we’ll explore the recent addition of Microsoft Graph activity logs, which has been made generally available.
Microsoft Graph activity logs provides a history of all Microsoft Graph API requests. In this blog, we’ll go over collection and analysis of these logs and share a few detection/hunting ideas. The goal is to create general awareness of this log source and show how it can be used effectively.
How to get Microsoft Graph activity logs
Microsoft Graph activity logs are not collected by default and are not available via an UI. You must collect the logs for storage and analysis via Log Analytics, Sentinel, or other SIEMs. Use the following steps to get these logs.
Note: You must have a Microsoft Entra ID P1 or P2 tenant license to collect the Microsoft Graph activity logs.
On the Azure portal, go to Microsoft Entra ID, and on the left pane, go to to Diagnostic Settings
- Choose the + Add diagnostic setting link.
- Enter a Diagnostic setting name.
-
Select MicrosoftGraphActivityLogs.
-
Under Destination details, configure one of the following locations to store the logs, and then choose Save.
- Send to Log Analytics workspace
- Archive to a storage account
- Stream to an event hub
- Send to partner solution
Figure 1: Screenshot showing the MicrosoftGraphActivityLogs Diagnostic setting.
Note: We recommend that you estimate the cost before you collect the logs. Depending on your tenant, there might be a high volume of logs generated.
Notable activity log fields
The output log contains many fields (for a full list, see what data is available in the activity logs). The following table lists the fields that are relevant to threat investigations.
Fields |
Details |
TenantId |
The Log Analytics workspace ID. |
TimeGenerated [UTC] |
The date and time the request was received. |
AppId |
The identifier of the application. |
IPAddress |
The IP address of the client from where the request occurred. |
ServicePrincipalId |
The identifier of the service principal making the request. |
RequestId |
The identifier that represents the request. |
RequestMethod |
The HTTP method of the event. |
ResponseStatusCode |
The HTTP response status code for the event. |
RequestUri |
The URI of the request. |
ResponseSizeBytes |
The size of the response in bytes. |
Roles |
The roles in token claims. |
Correlatable fields and functions
To enrich these logs, joining some of these fields with those from other tables can offer additional context.
User details: MicrosoftGraphActivityLogs can be joined with IdentityInfo logs by a common field (for example, UserID and AccountObjectId) to get further information about the user; however, this does not include information about service principals.
MicrosoftGraphActivityLogs
| where isnotempty(UserId)
| join kind=leftouter IdentityInfo on $left.UserId == $right.AccountObjectId
| where isnotempty(AccountUPN)
| project-reorder TimeGenerated, AppId, IPAddress, AccountUPN, AccountCreationTime, AssignedRoles, ServicePrincipalId, RequestId, RequestMethod, ResponseStatusCode, RequestUri, ResponseSizeBytes, Roles
Note: For data to appear in IdentityInfo, enable User and Entity Behavior Analytics (UEBA). For more information, see How to enable User and Entity Behavior Analytics.
RiskyUsers: Combining MicrosoftGraphActivityLogs with the table AadRiskyUser provides context on user risk details and risk levels.
MicrosoftGraphActivityLogs
| join AADRiskyUsers on $left.UserId == $right.Id
Geo IP information: You can get context on geo-location of the IP by using the function geo_info_from_ip_address.
MicrosoftGraphActivityLogs
| extend GeoIPInfo = geo_info_from_ip_address(IPAddress)
| extend country = tostring(parse_json(GeoIPInfo).country)
| extend state = tostring(parse_json(GeoIPInfo).state)
| extend city = tostring(parse_json(GeoIPInfo).city)
You can also derive the latitude and longitude of the IP addresses and plot them into a map on Azure Data Explorer. For more information, see Query data in Azure Monitor using Azure Data Explorer.
MicrosoftGraphActivityLogs
| where not (ipv4_is_private( IPAddress))
| extend GeoLocation = geo_info_from_ip_address(IPAddress)
| extend Latitude = toreal(['GeoLocation']['latitude'])
| extend Longitude = toreal(['GeoLocation']['longitude'])
| where isnotempty(Latitude) and isnotempty(Longitude)
| project Longitude, Latitude
| render scatterchart with (kind = map)
Function: One of the important fields in MicrosoftGraphActivityLogs is the ResourceURI field. Although this field is long and difficult to analyze, the parse_url() function can be used to divide the data to show an easily readable version of the Path field, making it easier to query and analyze.
MicrosoftGraphActivityLogs
| extend ParsedURI = parse_url(RequestUri)
| extend Path = tostring(ParsedURI.Path)
With this function, the path is clear, but it still includes API information, such as v1.0 or beta. During analysis or summarization, this might not add value. You can use the replace_string() function to remove that information.
MicrosoftGraphActivityLogs
| extend ParsedURI = parse_url(RequestUri)
| extend Path = tostring(ParsedURI.Path)
| extend FinalPath = replace_string(replace_string(Path ,'v1.0/',''),'beta/','')
Delegated vs. Application permissions
Before we run through scenarios to show how valuable these logs can be during an investigation of Microsoft Graph API misuse, it is important to understand the permissions used. Applications that use the Microsoft Graph API require either a Delegated or an Application permission.
Delegated permissions |
Application permissions |
|
User context |
Requires a signed-in user |
No user context needed |
Consent |
User consent (or admin on behalf of user) |
Admin consent required |
Scope |
Limited to user’s permissions |
Broader, organization-wide scope |
Typical use cases |
Interactive applications (web, mobile, desktop) |
Background services, daemons, administrative tools |
Examples |
Reading user email, updating user calendar |
Reading all user emails, managing directory data |
To learn more about delegated and application permissions, see Permissions and consent overview.
Additionally, registering an Entra application generates an application object and an associated service principal. The service principal defines the permissions the app has, specifying whether they are application permissions or delegated permissions, or both.
Attack patterns and hunting/detection opportunities
The Microsoft Graph API offers many functions that enable a wide range of activities across multiple operations and applications. Let's explore a few scenarios over the MITRE Attack matrix to understand how a benign API function can be misused if compromised. We'll examine parts of logs that might be significant during an investigation and identify opportunities for detecting and/or hunting such activities.
The purpose of this blog is not to provide a comprehensive list of all possible attacks and their respective detection/hunting opportunities. Instead, we’ll showcase a few examples to help you understand how to use the logs and to offer insights into building your own hunting strategy or use cases based on your organization.
The following examples require specific types of user permissions, which are specified as delegated, application, or both. The response codes in the queries are based on sample data. Please note that the queries are provided for reference purposes only.
Reconnaissance/Discovery
If a bad actor gains access to the tenant, the Microsoft Graph API can be misused to enable reconnaissance scenarios, such as enumerating users, groups, and roles, collecting metadata and configuration details, discovering misconfigured mailboxes, and retrieving app registrations, consents, and scopes. Numerous open-source tools are available to facilitate these activities.
Real world scenario: Microsoft Incident Response often assists external customers where tools for reconnaissance are used to collect data of a tenant to determine ways to elevate privileges.
Mitre technique: T1087
Attack: Reconnaissance
Permissions required: Various
RequestURI: Various
In this example scenario, we explored open-source reconnaissance tools, such as GraphRunner and AzureHound.
Query: The purpose of this query is to identify a surge in standard calls within a brief period that are characteristics of reconnaissance tools.
let calls = dynamic(["https://graph.microsoft.com/v1.0/users/<UUID>","https://graph.microsoft.com/v1.0/search/query","https://graph.microsoft.com/beta/policies/authorizationPolicy","https://graph.microsoft.com/v1.0/users","https://graph.microsoft.com/v1.0/groups","https://graph.microsoft.com/v1.0/groups/<UUID>/members","https://graph.microsoft.com/v1.0/servicePrincipals","https://graph.microsoft.com/v1.0/servicePrincipals/<UUID>","https://graph.microsoft.com/v1.0/applications","https://graph.microsoft.com/v1.0/servicePrincipals(appId='<UUID>')/appRoleAssignedTo","https://graph.microsoft.com/v1.0/organization","https://graph.microsoft.com/beta/servicePrincipals","https://graph.microsoft.com/beta/servicePrincipals/<UUID>/owners","https://graph.microsoft.com/beta/groups/<UUID>/owners","https://graph.microsoft.com/beta/groups/<UUID>/members","https://graph.microsoft.com/v1.0/servicePrincipals/<UUID>/appRoleAssignedTo","https://graph.microsoft.com/beta/applications/<UUID>/owners","https://graph.microsoft.com/beta/devices/<UUID>/registeredOwners","https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments","https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions","https://graph.microsoft.com/v1.0/devices","https://graph.microsoft.com/beta/users/<UUID>/roleManagement/directorytransitiveRoleAssignments","https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions/<UUID>","https://graph.microsoft.com/beta/roleManagement/directory/estimateAccess","https://graph.microsoft.com/beta/users"]);
MicrosoftGraphActivityLogs
| where ResponseStatusCode == '200'
| extend GeneralizedUri = replace_regex(RequestUri, @'\b[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}\b-[0-9a-fA-F]{12}', @'<UUID>')
| extend GeneralizedUri = replace_string(GeneralizedUri, @"//", @"/")
| extend GeneralizedUri = replace_string(GeneralizedUri, @"https:/", @"https://")
| extend GeneralizedUri = replace_regex(GeneralizedUri, @'\?.*$', @"")
| extend GeneralizedUri = replace_regex(GeneralizedUri, @'/$', @"")
| where GeneralizedUri in (calls)
| extend Id = iff(isempty(UserId), ServicePrincipalId, UserId)
| extend ObjectType = iff(isempty(UserId), "ServicePrincipal", "User")
| summarize MinTime=min(TimeGenerated), MaxTime=max(TimeGenerated), UniqueCalls=dcount(GeneralizedUri), CallsMade=count(), UserAgents=make_set(UserAgent) by IPAddress, bin(TimeGenerated, 2m), Id, ObjectType
| where datetime_diff('second', MaxTime, MinTime) < 100 and ((UniqueCalls >= 3 and CallsMade >= 40) or CallsMade > 100)
Note: Adjust the filter attributes as needed. You can also add more Microsoft Graph API requests to the calls array.
Figure 2: Reconnaissance results showing AzureHound and PowerShell usage.
Privilege escalation
Microsoft Graph provides several methods to escalate account privileges. These include assigning directory roles to users, adding users to privileged groups, creating service principals, and assigning roles.
Real world scenario: In an example of a customer engagement Microsoft IR was engaged on, a threat actor compromised a Service Principal of an Entra application that had the "RoleManagement.ReadWrite.Directory" role. Using these permissions, they granted a Global Administrator role to another compromised user identity.
Mitre technique: T1098.003
Application Compromised: Entra
Attack: Account manipulation: additional cloud roles
Permissions required: RoleManagement.ReadWrite.Directory (Delegated/Application)
ReqestURI: https://graph.microsoft.com/{v1.0 | beta}/{directoryRoles/{role-id}/members/$ref
For more details, see Add directory role member.
After an initial compromise, having specific privileges in an environment can allow for the assignment of higher privileges to other compromised accounts. In this example, an application with the "RoleManagement.ReadWrite.Directory" role was used to grant the Global Admin role to a user account under the actor’s control.
The following screenshot shows the roles assigned to the identity before the new role was added:
Figure 3: Screenshot showing a user with no role assigned.
The following screenshot shows that the identity has been granted Global Admin through Graph API:
Figure 4: Screenshot showing the Global Administrator role granted to the user.
Query: The following query detects role changes in Microsoft Graph activity logs, which also show when a role is added. Investigators should examine the result using audit logs or other available logs to provide further context and to distinguish between legitimate and unauthorized activity.
MicrosoftGraphActivityLogs
| where RequestUri has_all ("https://graph.microsoft.com/", "/directoryRoles/", "members/$ref")
| where RequestMethod == "POST"
| where ResponseStatusCode in ("204")
| extend Role = tostring(split(RequestUri, "/")[-3]) //Role can be looked up in Auditlogs
| project TimeGenerated, IPAddress, RequestUri, ResponseStatusCode, Role, UserAgent, AppId
Figure 5: Screenshot showing a successful response code (204).
Lateral movement
After compromising a user identity or a service principal, an actor can send phishing emails to users within the organization. This can potentially lead to the compromise of further identities and facilitate lateral movement.
Microsoft Threat Intelligence outlined a similar attack in their blog post on how threat actors misuse OAuth applications to automate financially driven attacks.
Mitre technique: T1534
Application misused: Exchange Online
Attack: Internal phishing
Permissions required: Mail.Send (Delegated/Application)
ReqestURI: https://graph.microsoft.com/{v1.0 | beta}/{me | users}/{id | userPrincipalName}/sendMail.
For more details, see user: sendMail.
In our scenario, a rogue application was created, and a phishing link was sent to a user. After the user's token was captured through the phish, the actor used delegated permissions to send emails to another user by using the sendMail API function.
Figure 6: Delegated permission abuse.
Query: This query detects the use of sendMail in the URI and lists every email sent using Microsoft Graph API. It distinguishes between delegated and application-based permissions and provides user information by combining it with the IdentityInfo table.
MicrosoftGraphActivityLogs
| where ResponseStatusCode == "202"
| where RequestUri endswith "/sendMail"
| extend EmailSentFrom = tostring(parse_url(RequestUri).Path).substring(1).split("/")[-2]
| extend Id = iff(isempty(UserId), ServicePrincipalId, UserId)
| extend Type = iff(isempty(UserId), "ServicePrincipal", "User")
| extend JoinKey = case(Type == "ServicePrincipal", EmailSentFrom, Type == "User", UserId, "")
| join kind=leftouter (IdentityInfo | extend JoinKey = AccountObjectId | summarize arg_max(TimeGenerated, *) by JoinKey ) on JoinKey
| extend AccountUPN = coalesce(AccountUPN, EmailSentFrom)
| project-reorder TimeGenerated, Type, AppId, MailAddress, RequestUri, ResponseStatusCode, UserAgent, AccountUPN
Note that the query retrieves details of successful mail submission requests to the server, but it doesn't verify that the actual mail was delivered.
Figure 7: SendMail operations request URI.
Query: Reviewing the app ID and service principal can help verify that the applications are allowed to send emails. The following query summarizes the emails sent by service principals in the past 30 days.
MicrosoftGraphActivityLogs
| where ResponseStatusCode == "202"
| where RequestUri endswith "/sendMail" and RequestUri has "/users/" //Looking for the user's API in terms of ServicePrincipal access
| extend EmailSentFrom = tostring(split(RequestUri, "/")[-2])
| extend Id = iff(isempty(UserId), ServicePrincipalId, UserId)
| extend Type = iff(isempty(UserId), "ServicePrincipal", "User")
| where Type == "ServicePrincipal"
| join kind=leftouter (IdentityInfo | summarize arg_max(TimeGenerated, *) by AccountObjectId ) on $left.EmailSentFrom == $right.AccountObjectId
| extend AccountUPN = coalesce(AccountUPN, EmailSentFrom)
| summarize EmailsSentCount=count(), SentFromUsers=make_set(AccountUPN), UserAgents=make_set(UserAgent) by AppId
Figure 8: SendMail operations count using ServicePrincipal.
Collection
The Microsoft Graph API includes functions that can be misused by a threat actor to access and read emails from users' mailboxes.
Real world scenario: In one customer engagement, a threat actor targeted a specific user’s mailbox by using delegated permissions and accessed multiple users' emails through an Entra application with broad permissions.
Mitre technique: T1114
Application misused: Exchange Online
Attack: Email collection
Permissions required: Mail.ReadBasic.All (Application), Mail.Read (Delegated/Application)
ReqestURI: https://graph.microsoft.com/{v1.0 | beta} /users/{user-id}/mailFolders/{folder-id}/messages
For more details, see List messages.
https://graph.microsoft.com/{v1.0 | beta} /users/UserID/messages
For more details, see Send Outlook messages from another user.
In our scenario, the threat actor abused an application with excessive permissions, which allowed them to gain unauthorized access to the mailboxes of users.
Query: The following query can be used to hunt for Microsoft Graph API calls which are used to read mail from a users’ mailbox. The application ID (AppId) represents the application that Microsoft Graph uses to access the emails.
MicrosoftGraphActivityLogs
| where RequestMethod == "GET"
| where RequestUri has_all ("https://graph.microsoft.com", "/users/", "/messages")
| where ResponseStatusCode in ("200")
| project AppId, UserAgent, RequestUri
Figure 9: Example of successful mail access.
Query: The query below reveals statistics about the applications or users used for reading emails, along with the number of unique mailboxes accessed and their respective timeframes. Note that this query also uses the IdentityInfo table.
MicrosoftGraphActivityLogs
| where RequestMethod == "GET"
| where RequestUri has_all ("https://graph.microsoft.com", "/users/", "/messages")
| where ResponseStatusCode == "200"
| extend Id = iff(isempty(UserId), ServicePrincipalId, UserId)
| extend ObjectType = iff(isempty(UserId), "ServicePrincipal", "User")
| extend MailboxTargetUPN = tostring(extract_all( @'https://graph.microsoft.com/v.../users/([^/]*)/', RequestUri)[0]) //Parses the AccountUPN
| extend UserGuid= tostring(extract_all( @'*.(\b[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}\b-[0-9a-fA-F]{12}).*', RequestUri)[0]) //Parses the object-ID of an targeted identity
| join kind=leftouter (IdentityInfo | where TimeGenerated > ago(30d) | summarize arg_max(TimeGenerated, *) by AccountObjectId | project TargetUPN=AccountUPN, AccountObjectId) on $left.UserGuid==$right.AccountObjectId
| extend TargetUPN = coalesce(TargetUPN, MailboxTargetUPN)
| summarize MinTime=min(TimeGenerated), MaxTime=max(TimeGenerated), MailBoxAccessCount=dcount(TargetUPN), Targets=make_set(TargetUPN) by AppId, ObjectType, Id
Figure 10: Results showing total unique Mailbox count.
Exfiltration
Actors can use Microsoft Graph to download sensitive files or data from any user’s OneDrive accounts or SharePoint. By illegitimately using file capabilities in Microsoft Graph, a bad actor can access and download confidential documents even without direct access to those files.
Real world scenario: In one of our customer engagements, Microsoft Incident Response observed compromised identities accessing files on SharePoint Online and OneDrive through Microsoft Graph API.
Mitre technique: T1567
Application misused: OneDrive/SharePoint
Attack: Download data
Permissions required: Files.ReadWrite (Delegated), Files.ReadWrite.All (Delegated/Application), Sites.ReadWrite.All (Delegated/Application)
ReqestURI:
https://graph.microsoft.com/{v1.0 | beta} /drives/{drive-id}/items/{item-id}/content
https://graph.microsoft.com/{v1.0 | beta} /groups/{group-id}/drive/items/{item-id}/content
https://graph.microsoft.com/{v1.0 | beta} /me/drive/root:/{item-path}:/content
https://graph.microsoft.com/{v1.0 | beta} /me/drive/items/{item-id}/content
https://graph.microsoft.com/{v1.0 | beta} /sites/{siteId}/drive/items/{item-id}/content
https://graph.microsoft.com/{v1.0 | beta} /sites/{siteId}/drives/{drive-id}/items/{item-id}/content
https://graph.microsoft.com/{v1.0 | beta} /users/{userId}/drive/items/{item-id}/content
For more details, see Download driveItem content.
In this scenario, the bad actor abused an application with Files Read/Write and Sites Read/Write permissions. These excessive permissions allow them to search through users’ OneDrive and SharePoint files to download confidential documents.
Query: The following query is a good starting point for investigating Microsoft Graph API calls related to download activities. Analyze the UserAgent and AppID to determine whether these activities are expected in your environment. Note that the Item ID cannot be resolved to identify the downloaded item, but CloudApp events can be correlated to provide further context for this download activity.
MicrosoftGraphActivityLogs
| where RequestMethod == "GET"
| where ResponseStatusCode in ("302", "200") // https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest-1.0&tabs=http#response, normal response code returns a "302 Found" response redirecting to a preauthenticated download URL.
| where RequestUri matches regex @"https://graph\.microsoft\.com/.*/items/.*/content" and RequestUri matches regex @"/drives?/.*" and RequestUri !has "/thumbnails/"
| project TimeGenerated, ResponseStatusCode, RequestMethod, IPAddress, UserAgent, RequestUri, AppId
Figure 11: SharePoint access.
Impact
When it comes to impact, Microsoft Graph API includes multiple functions that can be misused to allow unauthorized access to sensitive data and resources. This could lead to account access removals, data destruction, and/or resource hijacking. Such activities can significantly disrupt business operations, result in financial losses, and damage an organization’s reputation.
Mitre technique: T1531
Application misused: Entra
Attack: Account access removal
Permissions required: User.ReadWrite.All (Delegated/Application)
Request URI: https://graph.microsoft.com/v1.0/users/{user-id}
For more details, see User Delete
In this scenario, the bad actor abuses an application with the "User.ReadWrite.All" role to delete a user, disrupting business operations.
Query: The following query identifies delete requests and the associated User ID. AppID and UserAgent can be used for further investigation to determine if this behavior is expected.
MicrosoftGraphActivityLogs
| where RequestMethod == "DELETE"
| where RequestUri matches regex @"/users/[0-9a-fA-F-]{36}$"
| where ResponseStatusCode == "204"
| extend DeletedUserID = tostring(split(RequestUri, "/")[-1])
| join kind=leftouter (IdentityInfo | summarize arg_max(TimeGenerated, *) by AccountObjectId ) on $left.DeletedUserID == $right.AccountObjectId
| project-reorder TimeGenerated, ResponseStatusCode, RequestMethod, IPAddress, UserAgent, RequestUri, AppId, AccountDisplayName, AccountUPN
Figure 12: Account access removed.
Auditing the Microsoft Graph usage
Conducting regular audits of Entra applications that use the Microsoft Graph API can reveal whether an application has excessive permissions or if an application is accessing Microsoft Graph API unexpectedly or inappropriately. This can indicate a possible service principal compromise.
Auditing helps to create a safe list of approved applications with excessive permissions. Using this information, you can apply continuous monitoring to identify any new applications with high privileges, ensuring timely detection of potential security threats.
Query: The following query helps to identify the types of Entra applications that have accesses with high-impact permissions.
let PrivilegeAbuse = datatable (Type: string, Permission: string, Privilege: string, Reason: string) [
"Application","Mail.ReadWrite","High","BroadImpact",
"Application","Mail.Read","High","Collection",
"Application","Contacts","High","Phishing",
"Application","MailboxSettings","High","Phishing",
"Application","People","High","Phishing",
"Application","Files","High","Collection",
"Application","Notes","High","Collection",
"Application","Directory.AccessAsUser.All","High","Phishing",
"Application","user_impersonation","High","Phishing",
"Application","Application.ReadWrite.All","High","BroadImpact",
"Application","Directory.ReadWrite.All","High","BroadImpact",
"Application","Domain.ReadWrite.All","High","BroadImpact",
"Application","EduRoster.ReadWrite.All","High","BroadImpact",
"Application","Group.ReadWrite.All","High","BroadImpact",
"Application","Member.Read.Hidden","High","BroadImpact",
"Application","RoleManagement.ReadWrite.Directory","High","BroadImpact",
"Application","User.ReadWrite.All","High","BroadImpact",
"Application","User.ManageCreds.All","High","BroadImpact",
"Application","AppRoleAssignment.ReadWrite.All","High","PrivEscalation"
];
MicrosoftGraphActivityLogs
| where TimeGenerated between (ago(7d) .. now())
| extend ObjectType = iff(isempty(UserId), "ServicePrincipal", "User")
| where ObjectType == 'ServicePrincipal'
| extend RolesTemp = split(Roles, " ")
| mv-expand RolesTemp
| where RolesTemp has_any (( PrivilegeAbuse | distinct Permission ))
| extend Role = tostring(RolesTemp)
| summarize Calls=count(), MinTime=min(TimeGenerated), MaxTime=max(TimeGenerated) by AppId, Role
The final step is to evaluate the results of this query, compile a list of authorized Entra applications, and identify any unauthorized or recent usage. This can be done as a part of an ongoing monitoring and auditing process.
Conclusion
The Microsoft Graph API provides a unified endpoint to access a wide range of Microsoft 365 services, including Azure Active Directory, Outlook, Teams, OneDrive, and more. The potential misuse of the Microsoft Graph API poses a significant security risk to organizations. As described in this blog, malicious actors can and have used these APIs to gain access to sensitive data, disrupt operations, and exfiltrate information.
To counter these threats, it is crucial to collect and monitor Microsoft Graph API logs to identify suspicious activities, detect anomalies, and establish baseline usage patterns. Conduct regular audits of applications that use Microsoft Graph API to ensure that they strictly adhere to the principle of least privilege. By establishing a safe list of trusted applications and continuously monitoring high privileged accesses, organizations can swiftly detect and respond to threats and anomalies.
Proactive measures like these, combined with vigilant monitoring, are essential to safeguarding your organization against sophisticated cyber threats. As we always say: stay proactive, stay secure.