I was scanning through GitHub recently, and a commit that should or should not have remained public caught my eye. The commit included updates to the public docs and C# code (the language the modules are written in), which contained an additional parameter to the ‘Set-MgGraphOption’ cmdlet in the Microsoft Graph PowerShell SDK. The added parameter seemingly would have enabled proof of possession capabilities to bearer tokens used for authorisation in the Microsoft Graph SDK. The link to the commit can be found here.
Reading through the pull request, it seems that some concerns were raised about implementing the Proof-of-Possession capabilities, namely the introduction of the ‘Azure.Identity’ namespace.
Maybe the commenter misunderstood this as ‘Azure.Identity’ was already implemented in the release of v2 of the Microsoft Graph PowerShell SDK. Unfortunately, the introduction of the ‘Azure.Identity’ namespace at that time broke interoperability between the Graph PowerShell SDK and the Azure PowerShell module in PowerShell v5. That was soon fixed with the release of the Graph PowerShell SDK v2.1.0, as you can read here in the release notes.
Further down that pull request, you can see a recommendation which suggests that further development of this capability has been moved to an internal feed.
Almost 6 months after that comment was made, the pull request remains open under the Microsoft Graph PowerShell SDK public repo on GitHub.
Proof of Possession is an important concept for token security and should be high on Microsoft’s to-do list for implementation not only in the Graph SDK but across all resources in the Microsoft ecosystem.
But why is proof of possession important?
To understand why Proof of Possession is important, you first need to appreciate one of the core pitfalls of most public identity providers, such as Microsoft Entra. That those who hold the bearer token can access the resource, without any care in the world…!
To explain that a little more, a bearer token is a general class of token that grants access to a resource, to whoever has possession of that token, whether that be the good guy or the bad guy (i.e. literally whoever has access to it, the token ‘bearer’ so to speak). Importantly, access tokens, refresh tokens and ID tokens issued with Microsoft Entra ID are all bearer tokens and are all vulnerable to being stolen by default, due to no Proof of Possession.
But what about Primary Refresh Tokens? Well without digging into the weeds, PRTs (Primary Refresh Tokens) are special. They are special because PRTs are issued with a Proof of Possession key by default, which is encrypted by the device’s TPM chip (if it has one) and stored in LSASS to remain ‘secure’. Unfortunately, these keys can be extracted from memory (for devices without a TPM chip), decrypted and replayed, which was well-documented in Dirk-jan’s blog here. So not its not perfect for devices without a TPM chip, although while in 2020 devices with a TPM chip were still vulnerable, this was soon fixed by Microsoft in 2021 after CVE-2021-33779.
Regardless, the concept of PoP remains important. Without it, a stolen PRT token can simply bypass any potentially Conditional Access policy that is put in its way, this is because the PRT contains the device claim, allowing Conditional Access to evaluate it and mark it as compliant.
Access tokens and Refresh tokens obtained without the PRT can still be blocked by device state or location-based Conditional Access policies. But not if the PRT is stolen.
How does proof of possession work?
Let’s generally talk about proof of possession for a moment, out of the eyes of Microsoft Entra, but still specifically around the OAuth 2.0 DPoP standard (DPoP being an anagram for ‘Demonstrating Proof of Possession’).
The DPoP OAuth 2.0 standard provides specifications for implementing Proof of Possession, which is essentially a mechanism for token binding. In other words, it ensures that only the person requesting the token can use it. At a high level, the basic OAuth flow with DPoP can be seen here:
A. The client (application) sends a request to the authorisation server to obtain an access token. A DPoP proof is attached to the request in an HTTP header.
B. The authorisation server binds the access token to the public key claimed by the client in the DPoP Proof and sends it back to the client. This ensures the token cannot be used without providing possession of the private key.
C. The client then has to prove it has possession of the private key by adding a header to the request that carries the DPoP proof of that request. This is then sent to the resource server.
D. If the resource server receives a request with correct proof of possession, then the request can be served. If the DPoP proof is wrong, the server will refuse to serve the request.
However, DPoP is not the only method for sender-constrained access tokens (token-binding). mTLS, or Mutual Transport Layer Security provides a similar layer of protection but instead requires the use of PKI infrastructure which is a way less attractive prospect. mTLS standards were released back in 2020, whereas DPoP is arguably a fairly new standard, only being published in September 2023.
How Microsoft have implemented Proof of Possession
Well, let’s look again at Primary Refresh Tokens. Primary Refresh Tokens are a token type created by Microsoft specifically for Microsoft Entra authentication on Windows 10 and later, Windows Server, iOS or Android devices. PRTs are specifically issued to Microsoft first-party token brokers to enable sign sign-in. For example, WAM (Web Account Manager) is a first-party token broker that is often used when signing into PowerShell modules interactively, like the Az or Microsoft Graph PowerShell module. However, depending on the implementation of the module, either the PRT may be used for authorisation, or you may be forced to perform an interactive login (kind of not a security measure in this case..)
Anyway, PRTs are issued to users (only users), only on devices which are registered with Microsoft Entra ID. During the registration of a device, a set of cryptographic key pairs are generated, the private keys of these pairs are bound to the device’s TPM chip (if it has one) and the public key is sent to Microsoft Entra. These keys are then used to provide proof of possession when PRTs are issued to a user in either of these two scenarios:
- For Microsoft Entra Joined or hybrid joined devices: During the Windows login. In this case, the Microsoft Entra CloudAP plugin is the primary authority for the PRT.
- For Microsoft Entra registered devices: When a work account is added to the device. In this case, the Microsoft Entra WAM plugin is the primary authority for the PRT.
But why is all this talk about PRTs needed, all I want to know is how Microsoft is implementing Proof of Possession in all their other services or Microsoft Graph PowerShell?
Well, the Token Protection feature of Conditional Access that is currently in preview is Microsoft’s take on preventing token replay or similar attacks for other Microsoft Services. It is also clear that the Token Protection feature of Conditional Access takes advantage of already session-bound tokens by ensuring these tokens are used by applications when requesting access to a resource, i.e. the Primary Refresh Tokens.
It makes sense that PRTs are used for token protection, one because they have already established a cryptographically secure key pair between the device and Entra and two because the private key is protected by the device’s TPM chip. This also explains why the device requirements for Token Protection and PRT issuance are the same.
Is Token Protection worth it?
So, is token protection worth it? that age-old question…
There are still a few problems with how PRT’s and the Proof of Possession keys are secured on devices (which are used by Token Protection), here is a summary:
- If you have admin access to a device, then the PRT is vulnerable to replay attacks, regardless if it has a TPM or not.
- If your device doesn’t have a TPM, the PoP keys can be obtained from LSASS, which is even easier.
- Ensure all of your devices are suitable for business use and have a TPM chip.
- Ensure no users have admin-level access to their devices.
- Ensure devices are kept up to date.
Microsoft's answer to the PRT problem
I give credit where credit is due and Token Protection is still a pretty sweet deal, although still in preview and only available for a couple of core services. That being said, an answer still needs to be had for them in scenarios where the attacker does have full access to the user’s device and successfully extracts the Primary Refresh Token.
Ignoring the obvious preventative measures for a moment, Microsoft Entra ID Protection is the solution.
Microsoft Entra ID Protection is available within Entra P2, EMS E5, or the Microsoft E5/A5 licensing suites. It aims to block identity compromise in real-time using automated risk calculations, thread assessments and adaptive access policies powered by machine learning. It currently boasts up to 18 different sign-in risk detections and 9 user risk detections, one of those being a Possible attempt to access Primary Refresh Token (PRT).
In the event this risk is detected, the user in question will immediately move to a high-risk state, it is then your decision on how you action that risk, but ideally with a strong Conditional Access policy, token revocation flow or by disabling the user or device in Entra ID while the investigation occurs, depending on the claims in the token. Remember, if a PRT is stolen, the device or location state Conditional Access policies don’t matter. Microsoft provides a list of ways the PRT is invalidated here.
Back to Proof of Possession in Microsoft Graph PowerShell...
Circling back to the Microsoft Graph PowerShell module, it is unclear exactly how Proof of Possession will be implemented into the module. At a glance, I assume that the cmdlets will modify some environment variable that could do one of two things:
- Modify the authentication flow to use WAM and authenticate using the PRT token already cached on the device, OR
- Utilise an existing certificate key pair, with the private key on the client device and the public key uploaded to a custom app registration in Microsoft Entra.
The second option is seemingly less likely. The use cases are quite slim, as per this Microsoft Learn article, which demonstrates how to use Proof-of-Possession tokens to update certificates on an existing application, though an existing valid certificate is still required.
The first option, by way of utilising PRT, would still be somewhere mute, especially if the user had access to the device. In any case, an ideal scenario would be for this to be paired with Token Protection in Conditional Access, albeit, the policy is not yet available for ‘All resources’, which is required to target Microsoft Graph as a resource with Conditional access.
In summary, I have posed the question to the Microsoft Graph product team and am excited to learn about what I can share with the community about this.
My comment about Azure Identity was based on the fact that we do use Azure Identity as a wrapper around MSAL across all of our SDKs and this does make sense for language SDKs because it reduces the programmatic surface area that developers need to understand. However, in PowerShell, our use of Azure Identity is purely internal and doesn’t actually help us deliver value to our customers. In fact, it delays us from enabling new capabilities because we have to wait for the Azure Identity team to adopt new MSAL features before we can take advantage of them.
The MSAL library has done all the heavy lifting to enable the use of PoP tokens as is explained in this article https://learn.microsoft.com/en-us/entra/msal/dotnet/advanced/proof-of-possession-tokens
This article also calls out that dPoP isn’t currently used for PoP tokens. The mechanism is based this specification https://datatracker.ietf.org/doc/html/draft-ietf-oauth-signed-http-request-03
It also calls out that for confidential clients mTLS will be used.
To be honest, I’m feeling like I want to let all this settle down before we take a hard dependency on it.