Why using a FIDO2 security key is important

Microsoft offers a great variety of options to use as your primary authentication method, when signing-in with your Entra ID (Azure AD) identity using a browser.

Using conditional access you can further protect the accounts, enforcing the need for a second factor, device compliance, location based restrictions and many more configuration options.

Multi-factor authentication

For multi-factor authentication you can use any of the following methods.

  • SMS
  • Phone-call
  • TOTP Authenticator App
  • Microsoft Authenticator app

Please note that for Passwordless phone sign-in, Windows Hello for Business and FIDO2 security keys you cannot enforce a second factor since those methods are considered strong authentication methods.

SMS sign-in on the other hand is currently not supported with the requirement for a second factor.


It is very important to know that only three methods mentioned protect your users against phishing attacks.

  • Certificate-based authentication
  • Windows Hello for Business
  • FIDO2 security keys

Why this is the case is best explored using one of the freely available phishing toolkits. As always, seeing is believing and I always learn best when tinkering myself.

Meet evilginx2

evilginx2 is a man-in-the-middle attack framework used for phishing login credentials along with session cookies, which in turn allows to bypass 2-factor authentication protection.

This is the official description on the evilginx2 GitHub page. Together with Modlishka it was one of the first, easy to use reverse proxies, that demonstrated that a second factor alone does not protect the user from being phished.

Both projects do not attempt to fool the user with a website that looks almost like the original login website, they use reverse proxy techniques to forward the actual login website (e.g.[.]com) to the user. The phishing website is hosted on some domain the attacker has complete control over, so it is easy to get a valid certificate. evilginx2 even automates this process using the Let’s Encrypt certificate service.

The attacker now sits between the target service (Entra ID (Azure AD)) and the user and has complete access to the unencrypted data flowing through the proxy.

Instead of only harvesting the data from the POST requests, e.g. username and password, you can configure evilginx2 phishlets to also safe the session cookies. Depending on the lifetime of the session cookie, the attackers now has multiple days or just a few hours to use this information to impersonate the user. Since the initial authentication was done with a second factor, the attacker is not requested for this information a second time.

evilginx2 in action

This blog post does not cover the setup of evilginx2 nor does it provide ready to use phishlets.
Please refer to the original blog series or the projects site for more information on how to set up your test instance of evilginx2.

Lab setup

Our victim has received an email containing a phishing link with a convincing lure. Bob thinks he is accessing a OneDrive document.

The phishing domain used is portal.demo.nottrustedbutlegit[.]de which has no resemblance to[.]com. A real attacker would most likely choose something a bit closer to the original domain.

The attacker has configured evilginx2 to capture the session cookies as well as username and password whenever possible.

The Entra ID (Azure AD) administrator has created a conditional access policy that requires Bob to use MFA whenever he logs into any Cloud App.

Require multi-factor authentication

Also to protect her users she has set the session control “Persistent browser session” to “Never persistent”. This prevents the browser to save the session cookie and forces a new sign-in every time the browser is started.

Persistent browser session

Cookie lifetime

using username, password and 2FA

Bob clicks on the link and is requested to login to Entra ID (Azure AD). He enters his username and password and receives and pop-up on the Microsoft Authenticator app.

Since he expects this, he approves the sign-in and is successfully logged in. But instead of the document he is redirected to

using username and password

The attacker not only has captured the username and password but also the session cookie. Using the command sessions she can inspect all this information and even copy out the session cookie for future use.

Basic setup with evilginx2

Passwordless phone sign-in

In this scenario bob clicks the link, but has configured the Microsoft Authenticator App as a passwordless method.

Passwordless phone sign-in

The attacker was “only” able to capture the username and the session cookie. Read on to see why this makes no real difference.

Passwordless phone sign-in results

I’m aware that in this demo the session cookie was only partially captured. I fixed the phishlet and was able to capture the complete session cookie.

using FIDO2 security keys

Bob is using an external FIDO2 security key or the Windows built-in (>1903) FIDO capability of Windows Hello as a secure way to authenticate.

Bob is unable to use the FIDO2 security key to login to the phishing website.

using FIDO2 security keys

On the regular website of Microsoft ([.]com) he would be able to pick the credential he wants to use, but not on the fake domain the attacker is using.

Regular sign-in with FIDO2

Because of this, the attacker is not able to capture anything.

using FIDO2 security keys results

What’s the difference?

FIDO2/WebAuthn is using different methods to prevent phishing of credentials. In this attack a very simple, but super effective technique is used to prevent the user to sign-in.

The WebAuthn Client (the browser) compares the domain name with the Relying Party Identifier (RP ID) of the public keys in the FIDO2 security key. If a domain string matches it can be used as a method to sign-in. Otherwise the user is unable to use the credential stored in the key.

And since a machine is much better in comparing a string value to another string value, even a “good” phishing attack using a IDN domain that looks exactly like the target domain to the human eye is unable to bypass this protection.

Or could you easily sport the difference between and lοɡin.miсrο Not that easy, ain’t it?

The second one would read in punycode: xn–lin-9tb36o.xn–

On Bobs FIDO2 security there are two entries:

Relying Party Identifier Username

Only if the RP ID of the website is exactly the same as the domain name Bob can use his credentials to sign-in.

If you want more informations on FIDO2 and Windows Hello for Business, I recommend to watch the Ignite Session From Strong to Stronger: Phishing Resistant authentication methods (The Blueprint Files). Inbar Cizer Kobrinsky and Tarek Dawoud do a great job explaining the concept and techniques.


As show with the first two methods, the attacker was able to retrieve multiple parts of the credentials. The username, the password and the important parts of the session cookie (ESTSAUTH,ESTSAUTHPERSISTENT,SignInStateCookie).

Cookie information presented in evilginx2

Since the administrator of this tenant has put a conditional access policy in place to require MFA for each sign-in the username and password is not enough to take over the account.

But the session cookie contains all the information needed to authenticate the user and since the original session already presented a valid second factor or was using a strong authentication method like phone sign-in the attacker can easily use this cookie.

With the right browser extension and evilginx2 this is as easy as copy, paste and visit

Using a stolen cookie to authenticate against multiple services.

As you can see in this demo, the attacker not only has access to the website the victim was visiting, but any service accessible to the Entra ID (Azure AD) identity.

The attacker can even visit and register a FIDO2 security key as a new login method. Since this counts as strong authentication method, the attacker does not need to know the password of the user to login to the account from now on. And even a password change will not prevent the attacker from signing in.

Backdoor FIDO2 security key

That the administrator has configured “Persistent browser session” to “Never persistent” has little meaning in this case, evilginx2 does not honor the “Expires when the browsing session ends” information. The only difference this settings makes, it prevents the attacker from using the cookie after 24 hours.

Cookie expires when the browsing session ends

If the attacked users has the ability join a device to AAD or even enroll a new device to Intune the attacker could even get a compliant “company” device and now would be able to bypass security implementation based on device compliance. This is an attack Microsoft itself has documented in the blog post Evolved phishing: Device registration trick adds to phishers’ toolbox for victims without MFA.

Multi-phase phishing attack chain - Picture from

Conclusion, mitigation and prevention

Even if attackers can bypass some MFA methods with such an attack, MFA is still an important part of securing your identities. Based on the Cyber Signals report released by Microsoft in February of 2022 only 22% of Azure Active Directory identites use MFA. So currently convenience attackers will target those 78% of users who do not use MFA at all, while more targeted attacks might try to use advanced methods described here.

But this low number also mean that 78% of people can start with a better MFA option than out of band SMS!

  • SMS based 2FA is considered bad for a long time, because of attacks like SIM swapping
  • App based 2FA only based on TOTP can not provide additional information, but definitely use it if non of the following options is available
  • App based 2FA with Microsoft Authenticator is the best app-based option for Microsoft customers, since the developers can integrate new capabilities like additional context or GPS based location fencing
  • FIDO2 is the gold standard of 2FA methods.

It protects the user from signing-in to the wrong website, never sends credentials over the internet, has hardware backed cryptographic keys and needs real human interaction to work.
And the best thing is, you most likely have it already built into your device with Windows Hello. The FIDO Alliance recently published a new whitepaper presenting the next steps planned like using any smartphone as a FIDO key using Bluetooth or FIDO credential roaming.

But even if you cannot rollout strong MFA over night, Azure Active Directory as an IDP has more options to protect your users than MFA only. Device compliance is also a great tool to prevent unauthorized access.

Let’s see what you can do today:

Use FIDO2 security keys

The best way to mitigate this kind of phishing attacks is going passwordless with FIDO2 security keys.

You will find my blog series on going passwordless here.

Since you most likely will be unable to use FIDO2 for all accounts initially, you still can mitigate those attacks with conditional access and other methods.

Require device to be marked as compliant or Hybrid Entra ID (Azure AD) joined device

Since those access controls rely on additional telemetry from Intune and/or on a valid device certificate on the client device this type of phishing attack is prevented completely.

The attackers proxy cannot present or relay the device certificate of the victim and therefore cannot sign-in the user successfully.

not possible when device compliance is required.
Sign-in not possible on Hybrid Entra ID (Azure AD) joined device.

Using the new “Cross-tenant access settings” you can extend this requirement to guest accounts from trusted companies. This feature allows you to trust the home tenant of the user to handle MFA and relay device compliance or hybrid Entra ID (Azure AD) joined device states to your tenant.


Authentication and Conditional Access for External Identities

This feature is currently in preview.

Conditional Access Session Controls

frequency

Setting the conditional access setting “Sign-in frequency” to a shorter time will not prevent the attack itself, but will limit the time window which the attacker can use the phished session cookie. This session control should only be applied when accessing resources from unmanaged or shared devices. Otherwise you risk to many Sign-In requests and angry users.

Check out the documentation before you configure this.

In my testing, if the user choose to stay signed-in or if the session setting “Persistent browser session” is set to “Always persistent”, the session cookie is valid for 90 days.

This setting will apply as soon as the conditional access policy is active and will invalidate all sessions from targeted users if they don’t match the defined time range. It does not only apply to new sessions.

If you use “Persistent browser session” set to “Never persistent” the cookie is only valid for 24h hours and the browser will not store it after it’s closed.

Persistent browser session

You should apply this restriction to all administrative accounts.

This setting will not apply to already established sessions. You might want to consider revoking the sessions of all affected users to speed up the rollout.

Enable number matching

To avoid that the victim only has to click “Verify” in the authenticator app, number matching requires the user to enter a number displayed on the computer screen. This does not prevent the attack, but together with “additional context” it can make the user guess twice before clicking through.

Number matching is the default when using Passwordless phone sign-in.

Enable additional context in Microsoft Authenticator notifications

Additional context adds a map of the rough location, based in the ip address of the client, to the notification in the Microsoft Authenticator app. Combined with number matching this helps the user to identify poorly executed phishing attacks.


Can you spot the difference between the evilginx2 phishing attack and the normal sign-in?

Since the public ip address of the initiating client is used, this can cause confusion when using something like a central VPN oder proxy solution or when using VDI solutions like Azure virtual Desktop with a internet breakout in a Azure datacenter.

I wrote “poorly executed phishing attacks”, this is because if you have an adversary that has the resources, they could deploy the phishing server somewhere near the target to avoid easy detection.

Require MFA when registering security information or additional devices

Sadly this conditional access policy does not add additional protection when using the “Require multi-factor authentication” grant control.


When the initial sign-in was made with MFA the user will not be re-prompted for MFA again.


Hopefully this will change in the future.

Until then you could try to limit registering security information to company devices, which would make onboarding difficult if you have front line workers without a company owned device.

For the user action “Register or join devices” there is only the “Require multi-factor authentication” option available.


Revoke Sessions

If you suspect that a user is compromised, the first step should be to revoke all active sessions, check for newly added 2FA options and change the password.

The attacker can no longer use the phished cookies afterwards, without reauthentication.

The administrator can revoke all sessions with one click.

The end user is requested to sign-in again

Usage of a invalidated session also shows in the Sign-In logs

Additional methods and information

Joe Stocker @ITguySoCal has published a great blog post on this topic a few years ago, covering additional defenses like using trusted locations (IP based access control), Exchange Online Client Access Rules or the use of device compliance. The custom branding problem that evilginx2 had back then has been solved in the meantime and should not be considered a protection.

Check out the blog post for additional information.


You can use KQL to hunt for possible malicious activity.

Possible illegitimate addition of a security method

Search for all occurrences, were a user has initially logged into with one IP address and then registered a new security method from another ip address. You can change the maximum amount of time difference between those events, to minimize noise.

let SecurityInfoRegistered = AuditLogs
| where OperationName == "User registered security info"
| extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, UserPrincipalName, OperationName, ResultDescription, IPAddress;
| where ResultType == 0
| mv-expand todynamic(AuthenticationDetails)
| extend authenticationMethod = tostring(AuthenticationDetails.authenticationMethod)
| where authenticationMethod != "Previously satisfied"
| join kind=inner SecurityInfoRegistered on UserPrincipalName
| project-rename PossibleAttackerIPAddress = IPAddress1, SecurityInfoTimeGenerated = TimeGenerated1, SecurityInfoResultDescription = ResultDescription1, InitialLoginMethod = authenticationMethod
| extend TimeDifference = datetime_diff('second',TimeGenerated,SecurityInfoTimeGenerated)
| where TimeDifference < 0 and TimeDifference > -86400
| project TimeGenerated, TimeDifference, UserPrincipalName, OperationName, InitialLoginMethod, SecurityInfoResultDescription, IPAddress, PossibleAttackerIPAddress,  SecurityInfoTimeGenerated
| sort by TimeGenerated

Initial SignIn from different IP Address than the one used to register a new security information.

Administrators not using FIDO2 or WHfB

Identify every administrative account using a sign-in method other than FIDO2 or Windows Hello for Business (WHfB). You will need a conditional access policy that only applies to your administrative accounts to use this query.

I recommend to use the directory roles feature in conditional access to create such a conditional access policy.

let ConditionalAccessDisplayName = "Require MFA for administrators";
union isfuzzy=true SigninLogs, AADNonInteractiveUserSignInLogs
| where ResultType == 0
| mv-expand todynamic(AuthenticationDetails)
| extend authenticationMethod = tostring(AuthenticationDetails.authenticationMethod)
| where authenticationMethod !in ("FIDO2 security key","Previously satisfied","Windows Hello for Business")
| mv-expand ConditionalAccessPolicies_dynamic
| where ConditionalAccessPolicies_dynamic.displayName == ConditionalAccessDisplayName  and ConditionalAccessPolicies_dynamic.result != "notApplied"


evilginx2 and the evilginx2 logo is made by Kuba Gretzky (@mrgretzky) and it’s released under GPL3 license.

Hacker Cat designed by OpenMoji – the open-source emoji and icon project. License: CC BY-SA 4.0