Can you verify whether third-party applications adhere to the Identity platform best practices?

One of the resources I used in preparation for the latest version of my Entra ID service principals and applications reporting scripts was the Identity platform best practices article. In fact, some of the changes to the scripts are directly influenced by recommendations in said article, such as the inclusion of ReplyURIs and the various checks performed against them (which “calculates” the value of the HasBadURIs column). Others, I already had included in previous versions, such as the column to “detect” the use of insecure OAuth flows, or in general the recommendation to use certificates instead of client secrets.

Naturally, the question whether those best practices and recommendations are actually being implemented by ISVs or third-party applications in general, pops up. Unfortunately, there is no easy way to get such info, for a person outside of Microsoft at least. Still, the not-so-rare OAuth compromise events we’re witnessing in the past few years can give us some indication. And for some of these best practices, you can actually get data on your own. Which is what we will examine in this article.

By far the most interesting part, for me at least, is how the application handles authentication and the best practices around it. Here, we are focusing on web apps/web APIs/daemon apps, in other words apps leveraging the application permissions model and authenticating via the client credentials OAuth flow. The recommendation from the article above reads:

Protect and manage your confidential app credentials for web apps, web APIs and daemon apps. Use certificate credentials, not password credentials (client secrets). If you must use a password credential, don’t set it manually. Don’t store credentials in code or config, and never allow them to be handled by humans. If possible, use managed identities for Azure resources or Azure Key Vault to store and regularly rotate your credentials.

While we have no way of knowing how strictly the ISV adheres to the recommendation to rotate credentials or the one to avoid any human handling, we do have a way of reporting the method used for authentication. This information is contained within the Sign-in log events generated for service principal sign-ins, which nowadays we can access via the Entra ID portal, the Graph API or PowerShell. Here’s an example from one of my test tenants, where I’ve signed up for trials for few of the Microsoft 365 “management” apps out there:

Connect-MgGraph -Scopes AuditLog.Read.All

Get-MgBetaAuditLogSignIn -Filter "(signInEventTypes/any(t:t eq 'servicePrincipal'))" -All | select appDisplayName,clientCredentialType -Unique

You do have to use the beta endpoint, as service principals sing-ins are only exposed therein. And of course, you do need to have added the application within your own tenant, or as is the case with the examples above, multiple applications all part of the same “suite” and handling different tasks/having different permission requirements. Once we have this data, we can compare the values shown within the clientCredentialType field against the documentation:

Describes the credential type that a user client or service principal provided to Microsoft Entra ID to authenticate itself. You can review this property to track and eliminate less secure credential types or to watch for clients and service principals using anomalous credential types. The possible values are: none, clientSecret, clientAssertion, federatedIdentityCredential, managedIdentity, certificate, unknownFutureValue.

The results from this little experiment are summarized on the screenshot below:

SPsignIns1I have obfuscated the application names just to avoid “shaming” the corresponding vendors, but overall, the results are not great. The first obvious issue is that half of those apps use client secrets for authentication. While this method is certainly easier to leverage, it is not considered as secure, as the client secret after all is just a very long password. And while the chances for someone to brute-force a client secret are not that great, the secret itself can be leaked or captured if exposed in plain text over a compromised channel. Instead, certificates should be used as the preferred authentication method, as they offer additional security (the private key can be bound to a server or HSM and never travels over the wire, the authentication artefact itself is shorter lived than any secret, etc.).

The other, and not so obvious issue, is that even when using certificates, the vendors might have opted to implement their own wrapper, instead of leveraging the built-in MSAL methods. While this is an acceptable behavior, Microsoft recommends to stick to using the MSAL methods, as it allows them to implement additional security features on top of the module, such as the ones detailed in Alex Weinert’s session at last year’s Ignite. So what can we tell about such scenarios?

Unfortunately, not much. While relevant details do get exposed in the sign-in logs via the authenticationProcessingDetails property, it looks like said property is only ever populated for events corresponding to applications registered in your own tenant. I’m not sure whether this is by design, or might change in the future, but it’s certainly good info to have. Anyway, for the time being, you can only rely on such checks in the confines of your own tenant. Here’s how sample data might look like:

#Sign-in event via certificate and the ADAL library
"authenticationProcessingDetails": [

                    "key": "Azure AD App Authentication Library",
                    "value": "Family: ADAL Library: ADAL.NET Platform: .NET FW"

#Sign-in event via certificate and the MSAL library
"authenticationProcessingDetails": [

                    "key": "Azure AD App Authentication Library",
                    "value": "Family: MSAL Library: MSAL.NET Platform: .NET Core"

For any “assertion” created via “raw” HTTPS requests, no data is populated. In effect, we can filter any sign-in events where the ClientCredentialType value is set to clientAssertion and data about the authentication library used is missing, and flag them as “not so best practice”. Unfortunately, this is complicated by the aforementioned fact that the relevant data is missing from any “external” service principal sign-in attempt. If the authenticationProcessingDetails data ever starts to get populated for all SP sign-ins, this will allow us to better gauge whether the corresponding ISV adheres to this particular best practice.

Moving on, another thing we can check is how well the ISV follows Microsoft’s recommendations with respect to reply URIs. Said set is exposed as part of the replyUrls property on the service principal object, so data is readily available. Of course, one cannot reliably determine whether any given URI corresponds to production or demo/staging/whatever environment, but we can most certainly flag any entries with unsecure protocols, entries using “localhost” and so on. In fact, the updated version of my service principal reporting script does just that, so give it a go.

Get-MgServicePrincipal -All | select appDisplayName,appId, replyurls

So, going over the set of third-party applications in my demo tenant, unsurprisingly few “bad” entries popped out. Again, I’ll  avoid pointing at any specific vendor, but will actually mention our good friends at Microsoft…

Yet another piece of data to rely upon is the verification and/or certification status of the app. The former is easy to report on and is practically required for multi-tenant apps nowadays, so the value it gives us is minimal. The Microsoft 365 application certification status on the other hand should give you a much better understanding of how the application is designed, does it follow best practices, which regulations it complies with, and so on. With one tiny bit of a problem – the certification status is NOT exposed on the service principal object. So for the time being at least, you can only get it by checking the page above. Another issue with the certification status is that it’s not guaranteed to represent the findings of an actual audit. Still, it is a good source to rely on.

Speaking of good resources to leverage, if you have configured ingestion of the Graph API activity logs within your tenant, you might be able to expose additional details on each of the applications that have access to your tenant. Be warned though, the logs generate a lot, and I mean A LOT of data, so plan accordingly.

In summary, we outlined few methods that you can use to exert some control over how third-party applications access data within your own tenant (and claims made by their marketing and sales teams :D). Should you see something similar for any of the third-party applications you use, do make sure to challenge the processes and practices utilized by said vendor and demand some improvements. After all, it’s your data on the line.

It would be nice if Microsoft addresses some of these by enforcing stricter policies for multi-tenant applications, or give us the tools to do so, or at the very least, be able to audit them. For example, back when the app (credential) management policy was first released, I campaigned to have it cover service principles in a way that will actually stop/prevent third-party apps that do not adhere to the tenant-defined policies. Instead, Microsoft chose to enforce an Entra Workload ID Premium license requirement, effectively killing the feature. Good job Microsoft, keep on putting basic security features behind paywalls…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.