There is no doubt multi-factor authentication (MFA) is a simple and effective way to reduce account compromise, yet only 11% of all enterprise accounts use a MFA solution overall, accordingly to latest data from Microsoft.
Are there users without MFA enabled?
That's a question our security team wants answered and alerted. In our internal JupiterOne account, we have an audit/alert rule configured to check for user accounts without MFA enabled. The J1QL query for that particular rule is written as such:
Find User with mfaEnabled != true
that !(ASSIGNED|USES|HAS) mfa_device
This checks for any user that does not have the
mfaEnabled
attribute directly on the user entity and does not have a relationship (assigned/has/uses) to an MFA device entity.
Hmm ... so many false positives because of SSO
However, this query by itself has false positives in an environment with Single Sign On (SSO) configured.
For example, our users in Bitbucket, Jira, KnowBe4, and many other apps authenticate via Okta SSO. The users within these app accounts do not have MFA directly configured, and do not have an MFA device directly associated, because users never authenticate directly.
To accurately determine whether any user in these accounts does or does not use MFA for authentication, we will have to correlate them to SSO users and applications configured in Okta. For each user in an account connected via SSO, we have to check the following:
- An application is configured in Okta that connects it to the corresponding account
- A user from the target account has a matching active SSO user assigned to the SSO application in Okta
- The matching active user in Okta has MFA configured/assigned
This configuration scenario can be seen in the following graph, showing my Bitbucket user does not have MFA configured but it is actually authenticating via a connected Okta application, and my Okta user does have a number of MFA devices configured:
We need an automated way to "enrich" my Bitbucket user in the above example so that it is not included in the "user accounts without MFA enabled" alert rule. Here's how we make this work.
Reduce false positives using results from graph queries
First, we can use the following query to check for #1 and #2:
Find User with _key='<unique_key_of_the_user_entity>' as userA
that has Account
that connects okta_application
that assigned okta_user with active=true as userB
where
userA.name = userB.name or
userA.username = userB.username or
userA.email = userB.email
Enrichment: if this query returns a match, we can set
ssoUser: true
on the originating user entity (i.e.
userA
, the provider app user).
If your primary SSO provider is not Okta, replace
okta_application
and
okta_user
in the query above (and below) with the appropriate ones.
Next, we can use a second query to check for condition #3:
Find User with _key='<unique_key>' and ssoUser=true as userA
that has Account
that connects okta_application
that assigned okta_user with active=true as userB
that (assigned|has|uses) mfa_device
where
userA.name = userB.name or
userA.username = userB.username or
userA.email = userB.email
Enrichment: if this query returns a match, we can set
mfaEnabled: true
on the originating user entity (i.e.
userA
, the provider app user).
You may notice this second query is very similar to the first one. It has an additional relationship traversal at the end:
that (assigned|has|uses) mfa_device
So why not do this in one query? The reason is the additional filter at the beginning:
and ssoUser=true
This filter is added to ensure we only set the
mfaEnabled
property on user entities that were identified as SSO users in the previous step.
Graph enrichment FTW!
Here's my Bitbucket user after the enrichment is complete:
Now we repeat these two steps on each user that is not an Okta user (or whichever is your primary SSO provider — OneLogin, JumpCloud, etc.). This allows us to use our original "user accounts without MFA enabled" alert rule with all false positives virtually eliminated!
Review of the results validated that system accounts did not get the
ssoUser
or
mfaEnabled
flag, as intended, and identified a handful of users needing remediation.
Check out the entire code here.