Microsoft 365 Security — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Microsoft 365 Security (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Security checks are among the most high-value tasks an MSP can perform in a customer's M365 tenant. Account compromises, inadequate MFA coverage, and misconfigured mail rules are the leading causes of M365 security incidents. This skill covers the key checks and indicators that separate a secure tenant from a vulnerable one.
MFA enrollment lives on the authentication/methods endpoint per user. Users with only a passwordAuthenticationMethod entry have no MFA.
GET /v1.0/users/{userId}/authentication/methodsResponse — user WITH MFA:
{
"value": [
{
"@odata.type": "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod",
"id": "aad-method-id",
"displayName": "iPhone",
"createdDateTime": "2023-06-01T10:00:00Z"
},
{
"@odata.type": "#microsoft.graph.passwordAuthenticationMethod"
}
]
}Response — user WITHOUT MFA (vulnerable):
{
"value": [
{
"@odata.type": "#microsoft.graph.passwordAuthenticationMethod"
}
]
}Use Microsoft Graph Reports for tenant-wide MFA status:
GET /v1.0/reports/authenticationMethods/userRegistrationDetailsResponse per user:
{
"id": "user-guid",
"userPrincipalName": "[email protected]",
"isMfaRegistered": true,
"isMfaCapable": true,
"isSsprRegistered": false,
"methodsRegistered": ["microsoftAuthenticator", "softwareOath"]
}This is the fastest path to a full tenant MFA audit.
| Method | Security Level | Notes |
|---|---|---|
| FIDO2 hardware key | Highest | Phishing-resistant |
| Windows Hello for Business | Highest | Device-bound |
| Microsoft Authenticator (passwordless) | High | Number matching recommended |
| OATH hardware token | High | |
| Microsoft Authenticator (OTP) | Medium | Better than SMS |
| Software OATH (other app) | Medium | |
| SMS/Phone | Low | Susceptible to SIM swap |
| Password only | None | Unacceptable for business |
GET /v1.0/identityProtection/riskyUsers?$filter=riskState eq 'atRisk'&$select=id,userPrincipalName,riskLevel,riskState,riskLastUpdatedDateTimeRisk Levels: low, medium, high
GET /v1.0/auditLogs/signIns?$filter=riskLevelDuringSignIn ne 'none'&$select=userPrincipalName,riskLevelDuringSignIn,location,createdDateTime&$top=50POST /v1.0/identityProtection/riskyUsers/dismiss
Content-Type: application/json
{
"userIds": ["user-guid"]
}GET /v1.0/auditLogs/signIns?$filter=userPrincipalName eq '[email protected]'&$select=createdDateTime,userPrincipalName,ipAddress,location,status,clientAppUsed,riskLevelDuringSignIn&$top=20&$orderby=createdDateTime descKey fields for incident response:
status.errorCode: 0 = success, nonzero = failureipAddress and location for geolocation anomaliesclientAppUsed — legacy auth clients are high riskconditionalAccessStatus: notApplied = CA policy gapAttackers often create hidden inbox rules to forward mail or hide replies. Check for:
GET /v1.0/users/{userId}/mailFolders/inbox/messageRulesRed flags:
{
"displayName": "hidden rule",
"conditions": { "subjectContains": ["invoice"] },
"actions": { "forwardTo": [{ "emailAddress": { "address": "[email protected]" } }] },
"isEnabled": true
}Legacy auth protocols (IMAP, POP3, SMTP AUTH, basic auth) bypass MFA. Identify users still using them:
GET /v1.0/auditLogs/signIns?$filter=clientAppUsed eq 'IMAP' or clientAppUsed eq 'POP3' or clientAppUsed eq 'Exchange ActiveSync'&$select=userPrincipalName,clientAppUsed,createdDateTime&$top=100Recommended: Block legacy auth via Conditional Access policy.
Check if CA policies are configured:
GET /v1.0/identity/conditionalAccess/policies?$select=id,displayName,state,conditions,grantControls`state` values: enabled, disabled, enabledForReportingButNotEnforced (report-only)
MSP baseline CA policies to verify exist:
Get the tenant's Microsoft Secure Score:
GET /v1.0/security/secureScores?$top=1Response:
{
"value": [{
"currentScore": 52.4,
"maxScore": 120.0,
"averageComparativeScores": [
{ "basis": "AllTenants", "averageScore": 38.2 }
]
}]
}Score improvement recommendations:
GET /v1.0/security/secureScoreControlProfiles?$select=title,maxScore,implementationStatus,controlCategoryWhen a user reports suspicious activity, check these in order:
| Check | Command | Red Flag |
|---|---|---|
| Recent sign-ins | GET /auditLogs/signIns | Unfamiliar IP, country, time |
| MFA changes | GET /auditLogs/directoryAudits | MFA method added/removed |
| Inbox rules | GET /mailFolders/inbox/messageRules | External forwarding |
| Sent items | GET /messages from Sent folder | Phishing sent from account |
| OAuth apps | GET /oauth2PermissionGrants | Unknown app granted access |
POST /v1.0/users/{id}/revokeSignInSessions| Task | Microsoft Graph Permission |
|---|---|
| MFA registration report | UserAuthenticationMethod.Read.All |
| Authentication methods | UserAuthenticationMethod.Read.All |
| Sign-in logs | AuditLog.Read.All |
| Risky users | IdentityRiskyUser.Read.All (P2) |
| Conditional access | Policy.Read.All |
| Security score | SecurityEvents.Read.All |
| Revoke sessions | Directory.ReadWrite.All |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.