npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
MITRE ATLAS
Prerequisites
- Understanding of zero trust principles (NIST SP 800-207)
- Familiarity with identity providers (Azure AD, Okta, Ping Identity)
- Knowledge of authentication protocols (SAML 2.0, OIDC, FIDO2)
- Understanding of MFA and passwordless authentication
Overview
Identity is the foundational pillar of zero trust architecture. NIST SP 800-207 mandates that all resource authentication and authorization are dynamic and strictly enforced before access is allowed. Identity verification in zero trust goes beyond traditional username/password by implementing continuous, risk-adaptive authentication using multiple signals including device posture, behavioral biometrics, location, and network context.
This skill covers implementing phishing-resistant MFA, continuous identity verification, risk-based conditional access, and identity governance aligned with the CISA Zero Trust Maturity Model Identity Pillar.
When to Use
- When deploying or configuring implementing identity verification for zero trust capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Familiarity with zero trust architecture concepts and tools
- Access to a test or lab environment for safe execution
- Python 3.8+ with required dependencies installed
- Appropriate authorization for any testing activities
Architecture
Identity Verification Flow
User Access Request
│
v
┌───────────────────────┐
│ Primary Authentication │
│ - FIDO2/WebAuthn key │
│ - Certificate-based │
│ - Passwordless │
└──────────┬────────────┘
v
┌───────────────────────┐
│ Contextual Assessment │
│ - Device posture │
│ - Network location │
│ - Geo-velocity check │
│ - Time of access │
│ - Behavioral baseline │
└──────────┬────────────┘
v
┌───────────────────────┐
│ Risk Scoring Engine │
│ - Aggregate signals │
│ - Calculate risk score │
│ - Compare to threshold │
└───┬──────────┬────────┘
│ │
Low Risk High Risk
│ │
v v
┌────────┐ ┌──────────────┐
│ Grant │ │ Step-up Auth │
│ Access │ │ - Hardware key│
│ │ │ - Biometric │
│ │ │ - Manager OK │
└────────┘ └──────────────┘Identity Provider Architecture
- Primary IdP: Azure AD / Okta / Ping Identity for centralized identity management
- FIDO2 Authenticators: Hardware security keys (YubiKey) or platform authenticators (Windows Hello, Touch ID)
- Risk Engine: Adaptive access using identity threat detection (Microsoft Entra ID Protection, Okta ThreatInsight)
- Identity Governance: Lifecycle management, access reviews, just-in-time provisioning
- Privileged Identity: Separate verification for elevated access (CyberArk, BeyondTrust)
Key Concepts
Phishing-Resistant MFA
FIDO2/WebAuthn eliminates phishable credentials by binding authentication to the origin domain. Hardware security keys and platform authenticators provide cryptographic proof of identity without transmitting secrets.
Continuous Identity Verification
Rather than authenticating once at session start, zero trust requires ongoing verification through session token evaluation, behavioral analytics, and periodic re-authentication challenges based on risk signals.
Risk-Based Conditional Access
Conditional access policies evaluate multiple signals (user risk level, sign-in risk, device compliance, location) to dynamically adjust authentication requirements and access grants.
Identity Threat Detection
AI-driven analytics detect compromised identities through impossible travel detection, anomalous sign-in patterns, credential stuffing detection, and token replay attacks.
Workflow
Phase 1: Identity Infrastructure
-
Consolidate Identity Providers
- Audit all identity sources across the organization
- Federate to a single authoritative IdP using SAML 2.0 or OIDC
- Configure SCIM for automated provisioning and deprovisioning
- Eliminate local accounts and shared credentials
-
Deploy Phishing-Resistant MFA
- Enroll all users in FIDO2/WebAuthn with hardware security keys
- Configure platform authenticators (Windows Hello for Business, macOS Touch ID)
- Disable SMS and voice call as MFA methods (phishable)
- Create conditional access policy requiring phishing-resistant methods for all sign-ins
-
Configure Conditional Access Policies
- Require compliant device for access to sensitive applications
- Block legacy authentication protocols (basic auth, IMAP, POP3)
- Require MFA for all users from untrusted locations
- Enforce session time limits with re-authentication
- Block or require additional verification for high-risk sign-ins
Phase 2: Risk-Based Authentication
-
Enable Identity Threat Detection
- Activate Microsoft Entra ID Protection or Okta ThreatInsight
- Configure risk levels: low (allow), medium (require MFA), high (block and investigate)
- Enable impossible travel detection and anomalous token alerts
- Integrate identity risk signals with SIEM/SOAR
-
Implement Step-Up Authentication
- For sensitive operations (privilege elevation, financial transactions), require additional verification
- Configure step-up policies: re-authenticate with hardware key
- Integrate with PAM for privileged session approval workflows
- Log all step-up events for audit trail
Phase 3: Continuous Verification
-
Deploy Continuous Access Evaluation (CAE)
- Enable Continuous Access Evaluation Protocol (CAEP) for real-time token revocation
- Configure critical event triggers: user disabled, password changed, location change
- Test that token revocation occurs within minutes (not hours) of security event
- Monitor CAE event logs for operational health
-
Implement Session Controls
- Configure session duration limits based on application sensitivity
- Enable sign-in frequency controls (re-authenticate every N hours)
- Implement persistent browser session controls
- Configure app-enforced restrictions for unmanaged devices
Phase 4: Identity Governance
-
Automate Identity Lifecycle
- Configure joiner-mover-leaver workflows with HR system integration
- Automate access provisioning based on role and department
- Enable just-in-time access for temporary elevated permissions
- Configure automatic access expiration for contractors and guests
-
Implement Access Reviews
- Schedule quarterly access certification campaigns
- Configure automated reminders and escalation
- Require manager approval for continued access
- Auto-revoke access for unreviewed certifications
Validation Checklist
- Single authoritative IdP with all applications federated
- FIDO2/WebAuthn enrolled for all users
- SMS and voice MFA methods disabled
- Legacy authentication protocols blocked
- Conditional access policies enforced for all applications
- Identity threat detection active with risk-based policies
- Continuous Access Evaluation enabled and tested
- Step-up authentication configured for sensitive operations
- Identity lifecycle automated with HR integration
- Quarterly access reviews scheduled and operational
- Identity events streaming to SIEM
References
- NIST SP 800-207: Zero Trust Architecture
- NIST SP 800-63B: Digital Identity Guidelines - Authentication
- CISA Zero Trust Maturity Model v2.0 - Identity Pillar
- FIDO Alliance WebAuthn Specification
- Microsoft Entra Conditional Access Documentation
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.8 KB
API Reference: Implementing Identity Verification for Zero Trust
CISA Zero Trust Maturity Model - Identity Pillar
| Level | Description | Requirements |
|---|---|---|
| Traditional | Password-based, static policies | Basic auth |
| Initial | MFA deployed, basic conditional access | MFA for all users |
| Advanced | Phishing-resistant MFA, risk-based | FIDO2, risk signals |
| Optimal | Continuous verification, passwordless | Behavioral analytics |
Azure AD Conditional Access API
import requests
headers = {"Authorization": "Bearer <token>"}
policies = requests.get(
"https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies",
headers=headers).json()FIDO2/WebAuthn Registration
const credential = await navigator.credentials.create({
publicKey: {
rp: { name: "Example Corp" },
user: { id: userId, name: email, displayName: name },
challenge: serverChallenge,
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
authenticatorSelection: { residentKey: "required" },
}
});Conditional Access Signals
| Signal | Source | Zero Trust Level |
|---|---|---|
| Device compliance | MDM/Intune | Initial |
| Location/IP | Network context | Initial |
| User risk | Identity Protection | Advanced |
| Sign-in risk | Real-time analysis | Advanced |
| Session behavior | UEBA | Optimal |
Okta Authentication Policies API
curl -X GET "https://DOMAIN.okta.com/api/v1/policies?type=ACCESS_POLICY" \
-H "Authorization: SSWS <token>"References
- CISA Zero Trust Maturity Model: https://www.cisa.gov/zero-trust-maturity-model
- NIST SP 800-207: https://csrc.nist.gov/pubs/sp/800/207/final
- FIDO Alliance: https://fidoalliance.org/fido2/
standards.md3.8 KB
Standards and Frameworks Reference
NIST SP 800-207: Zero Trust Architecture - Identity
Core Identity Tenets
- All resource authentication and authorization are dynamic and strictly enforced before access is allowed
- The enterprise collects as much information as possible about the current state of assets, network infrastructure, and communications and uses it to improve its security posture
- Access decisions should consider behavioral attributes, environmental attributes, and the identity of the requester
Policy Engine Identity Inputs
The Policy Engine (PE) uses the following identity-related inputs:
- User identity and attributes from the IdP
- Device identity and posture from the endpoint management system
- Behavioral attributes from user and entity behavior analytics (UEBA)
- Environmental attributes (location, time, network)
NIST SP 800-63B: Digital Identity Guidelines
Authentication Assurance Levels (AAL)
| AAL | Description | Methods | Zero Trust Mapping |
|---|---|---|---|
| AAL1 | Some assurance | Single-factor (password) | Insufficient for ZT |
| AAL2 | High confidence | Multi-factor (push notification, OTP) | Minimum for ZT |
| AAL3 | Very high confidence | Hardware-based (FIDO2, PIV card) | Target for ZT |
Phishing-Resistant Authenticators
- FIDO2/WebAuthn: Cryptographic authentication bound to origin domain
- PIV/CAC smart cards: Certificate-based authentication
- Not phishing-resistant: SMS OTP, voice calls, push notifications, TOTP
CISA Zero Trust Maturity Model v2.0 - Identity Pillar
| Maturity Level | Authentication | Identity Store | Risk Assessment | Visibility |
|---|---|---|---|---|
| Traditional | Password + basic MFA | Multiple disconnected stores | None | Basic audit logs |
| Initial | MFA for all users | Federated IdP | Static risk rules | Centralized auth logs |
| Advanced | Phishing-resistant MFA | Single authoritative IdP with SCIM | Risk-based conditional access | Identity analytics |
| Optimal | Continuous verification | Automated lifecycle governance | AI-driven threat detection | Real-time UEBA |
FIDO Alliance Standards
FIDO2 / WebAuthn
- W3C Web Authentication specification for passwordless authentication
- Public-key cryptography: private key never leaves the authenticator
- Origin-bound: authentication is cryptographically tied to the service domain
- Resistant to phishing, replay, and man-in-the-middle attacks
Passkeys
- Evolution of FIDO2 for consumer and enterprise use
- Synced across devices via platform credential managers (iCloud Keychain, Google Password Manager)
- Discoverable credentials eliminate need to remember usernames
Microsoft Entra (Azure AD) Identity Protection
Risk Detection Categories
| Risk Type | Detection | Response |
|---|---|---|
| Anonymous IP | Sign-in from anonymous proxy/VPN | Require MFA |
| Atypical travel | Impossible travel between sign-in locations | Block + investigate |
| Malware-linked IP | Sign-in from known malicious IP | Block |
| Unfamiliar sign-in | Unusual sign-in properties | Step-up auth |
| Leaked credentials | Credentials found in dark web dumps | Force password reset |
| Token anomaly | Unusual token characteristics | Revoke session |
Continuous Access Evaluation Protocol (CAEP)
- Real-time token revocation on security events
- Critical events: user disabled, password changed, high risk detected
- Reduces token lifetime gap from hours to near real-time
- Supported by Microsoft 365, Exchange Online, SharePoint Online
Okta Identity Security
Okta ThreatInsight
- Pre-authentication threat detection using IP reputation
- Credential stuffing protection
- Bot detection and rate limiting
- Anomalous location and device detection
Okta FastPass
- Passwordless, phishing-resistant authentication
- Device-bound biometric verification
- Continuous device trust assessment
- No shared secrets transmitted over network
workflows.md6.8 KB
Identity Verification Workflows
Workflow 1: Zero Trust Authentication Flow
User Initiates Access
│
v
┌─────────────────────────┐
│ 1. Pre-Authentication │
│ - Check IP reputation │
│ - Rate limit evaluation │
│ - Bot detection │
│ - Geo-blocking check │
└──────────┬──────────────┘
v
┌─────────────────────────┐
│ 2. Primary Authentication│
│ - FIDO2 key challenge │
│ - Biometric verification │
│ - Certificate validation │
│ - Passwordless flow │
└──────────┬──────────────┘
v
┌─────────────────────────┐
│ 3. Context Assessment │
│ - Device compliance │
│ - Network location │
│ - Time of access │
│ - Behavioral baseline │
│ - Previous session state │
└──────────┬──────────────┘
v
┌─────────────────────────┐
│ 4. Risk Calculation │
│ - User risk level │
│ - Sign-in risk level │
│ - Aggregate score │
└───┬──────────┬──────┬───┘
│ │ │
LOW MED HIGH
│ │ │
v v v
┌──────┐ ┌────────┐ ┌────────┐
│Grant │ │Step-Up │ │Block + │
│Token │ │ Auth │ │Alert │
└──────┘ └────────┘ └────────┘Workflow 2: Continuous Access Evaluation
Active Session
│
v
┌──────────────────────────────┐
│ Continuous Monitoring Loop │
│ │
│ ┌─── Check every N minutes ──┐
│ │ │
│ │ ┌─────────────────────┐ │
│ │ │ Signal Collection │ │
│ │ │ - Device compliance │ │
│ │ │ - User risk change │ │
│ │ │ - Location shift │ │
│ │ │ - Behavior anomaly │ │
│ │ └──────────┬──────────┘ │
│ │ v │
│ │ ┌─────────────────────┐ │
│ │ │ Critical Events │ │
│ │ │ - Account disabled │ │
│ │ │ - Password changed │ │
│ │ │ - MFA registration │ │
│ │ │ - Admin revocation │ │
│ │ └──────────┬──────────┘ │
│ │ v │
│ │ ┌─────────────────────┐ │
│ │ │ Re-Evaluate Access │ │
│ │ │ - Recalculate risk │ │
│ │ │ - Apply policy │ │
│ │ └───┬─────────┬───────┘ │
│ │ │ │ │
│ │ Continue Revoke │
│ │ Session Token │
│ └──────┘ │ │
│ v │
│ ┌──────────────┐ │
│ │ Force Re-Auth│ │
│ │ or Terminate │ │
│ └──────────────┘ │
└──────────────────────────────┘Workflow 3: FIDO2 Enrollment
Admin Initiates Enrollment Campaign
│
v
┌──────────────────────────┐
│ 1. User Notification │
│ - Email with instructions │
│ - Self-service portal URL │
│ - Deadline for enrollment │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 2. User Self-Service │
│ - Authenticate with │
│ existing credentials │
│ - Register security key │
│ (YubiKey, Titan key) │
│ - Register platform auth │
│ (Windows Hello, TouchID)│
│ - Register backup method │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 3. Verification │
│ - Test sign-in with FIDO2 │
│ - Confirm backup works │
│ - Record key serial/ID │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 4. Policy Enforcement │
│ - Enable phishing-resist │
│ conditional access │
│ - Disable legacy MFA │
│ - Monitor compliance rate │
└──────────────────────────┘Workflow 4: Compromised Identity Response
Identity Threat Detected
│
v
┌──────────────────────────┐
│ 1. Detection Signal │
│ - Impossible travel │
│ - Leaked credentials │
│ - Token anomaly │
│ - Behavioral deviation │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 2. Automated Response │
│ - Revoke all sessions │
│ - Disable account │
│ - Trigger SOAR playbook │
│ - Notify SOC analyst │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 3. Investigation │
│ - Review sign-in logs │
│ - Check accessed resources│
│ - Correlate with EDR data │
│ - Interview user │
└──────────┬───────────────┘
v
┌──────────────────────────┐
│ 4. Remediation │
│ - Reset all credentials │
│ - Re-enroll FIDO2 keys │
│ - Review and restrict │
│ access permissions │
│ - Re-enable account │
│ - Update detection rules │
└──────────────────────────┘Scripts 2
agent.py8.6 KB
#!/usr/bin/env python3
"""Agent for assessing identity verification controls in zero trust architecture."""
import json
import argparse
from datetime import datetime
from collections import Counter
CISA_ZT_IDENTITY_LEVELS = {
"traditional": {
"description": "Password-based auth, static policies",
"score": 1,
},
"initial": {
"description": "MFA deployed, basic conditional access",
"score": 2,
},
"advanced": {
"description": "Phishing-resistant MFA, risk-based access",
"score": 3,
},
"optimal": {
"description": "Continuous verification, passwordless, behavioral analytics",
"score": 4,
},
}
def assess_authentication_methods(auth_config):
"""Assess authentication method strength against zero trust requirements."""
findings = []
methods = auth_config.get("methods", auth_config.get("authentication", {}))
if isinstance(methods, dict):
mfa_enabled = methods.get("mfa_enabled", False)
phishing_resistant = methods.get("phishing_resistant_mfa", False)
passwordless = methods.get("passwordless", False)
fido2 = methods.get("fido2_enabled", False)
sso = methods.get("sso_enabled", False)
if not mfa_enabled:
findings.append({"control": "MFA", "status": "MISSING",
"severity": "CRITICAL"})
elif not phishing_resistant:
findings.append({"control": "Phishing-resistant MFA", "status": "MISSING",
"severity": "HIGH",
"recommendation": "Deploy FIDO2 or certificate-based auth"})
if not fido2:
findings.append({"control": "FIDO2/WebAuthn", "status": "NOT_DEPLOYED",
"severity": "MEDIUM"})
if not passwordless:
findings.append({"control": "Passwordless authentication",
"status": "NOT_DEPLOYED", "severity": "MEDIUM"})
if not sso:
findings.append({"control": "SSO", "status": "NOT_DEPLOYED",
"severity": "HIGH"})
return findings
def assess_conditional_access(policies_path):
"""Assess conditional access policies for zero trust alignment."""
with open(policies_path) as f:
policies = json.load(f)
items = policies if isinstance(policies, list) else policies.get("policies", [])
findings = []
required_signals = ["device_compliance", "location", "risk_level",
"application", "user_group"]
covered_signals = set()
for policy in items:
conditions = policy.get("conditions", {})
for signal in required_signals:
if conditions.get(signal) or signal in str(conditions):
covered_signals.add(signal)
if policy.get("grant_controls", {}).get("operator") == "OR":
findings.append({
"policy": policy.get("name", ""),
"issue": "Grant controls use OR (should be AND)",
"severity": "HIGH",
})
if not policy.get("state", "").lower() in ("enabled", "on"):
findings.append({
"policy": policy.get("name", ""),
"issue": "Policy not enabled",
"severity": "MEDIUM",
})
missing = set(required_signals) - covered_signals
for signal in missing:
findings.append({
"control": f"Conditional access signal: {signal}",
"status": "NOT_COVERED",
"severity": "HIGH",
})
return {"findings": findings, "covered_signals": list(covered_signals),
"missing_signals": list(missing)}
def assess_identity_maturity(config):
"""Assess identity pillar maturity against CISA Zero Trust Maturity Model."""
scores = {}
categories = {
"authentication": {
"checks": ["mfa_enforced", "phishing_resistant_mfa", "passwordless",
"continuous_auth"],
},
"identity_stores": {
"checks": ["centralized_idp", "cloud_identity", "directory_sync",
"identity_federation"],
},
"risk_assessment": {
"checks": ["risk_based_access", "behavioral_analytics",
"impossible_travel_detection", "session_risk_scoring"],
},
"visibility": {
"checks": ["identity_audit_logging", "real_time_monitoring",
"identity_analytics_dashboard", "automated_anomaly_detection"],
},
}
for category, info in categories.items():
implemented = sum(1 for check in info["checks"]
if config.get(category, {}).get(check, False))
total = len(info["checks"])
ratio = implemented / total if total else 0
if ratio >= 0.9:
level = "optimal"
elif ratio >= 0.6:
level = "advanced"
elif ratio >= 0.3:
level = "initial"
else:
level = "traditional"
scores[category] = {
"implemented": implemented,
"total": total,
"ratio": round(ratio, 2),
"maturity_level": level,
"score": CISA_ZT_IDENTITY_LEVELS[level]["score"],
}
avg_score = sum(s["score"] for s in scores.values()) / len(scores) if scores else 0
for level_name, level_info in CISA_ZT_IDENTITY_LEVELS.items():
if level_info["score"] >= avg_score:
overall_level = level_name
break
else:
overall_level = "traditional"
return {"categories": scores, "overall_score": round(avg_score, 1),
"overall_level": overall_level}
def analyze_auth_events(events_path):
"""Analyze authentication events for zero trust insights."""
with open(events_path) as f:
events = json.load(f)
items = events if isinstance(events, list) else events.get("events", [])
by_method = Counter(e.get("auth_method", "unknown") for e in items)
by_result = Counter(e.get("result", "unknown") for e in items)
risky = [e for e in items if e.get("risk_level", "").lower() in ("high", "critical")]
mfa_bypassed = [e for e in items if e.get("mfa_bypassed", False)]
return {
"total_events": len(items),
"by_method": dict(by_method),
"by_result": dict(by_result),
"high_risk_events": len(risky),
"mfa_bypass_events": len(mfa_bypassed),
"password_only_rate": round(
by_method.get("password", 0) / len(items) * 100, 1) if items else 0,
}
def main():
parser = argparse.ArgumentParser(description="Zero Trust Identity Verification Agent")
parser.add_argument("--auth-config", help="Authentication config JSON")
parser.add_argument("--policies", help="Conditional access policies JSON")
parser.add_argument("--maturity-config", help="Identity maturity assessment config JSON")
parser.add_argument("--auth-events", help="Authentication events log JSON")
parser.add_argument("--action", choices=["auth", "policies", "maturity", "events", "full"],
default="full")
parser.add_argument("--output", default="zt_identity_report.json")
args = parser.parse_args()
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
if args.action in ("auth", "full") and args.auth_config:
with open(args.auth_config) as f:
config = json.load(f)
findings = assess_authentication_methods(config)
report["results"]["auth_assessment"] = findings
critical = sum(1 for f in findings if f.get("severity") == "CRITICAL")
print(f"[+] Auth findings: {len(findings)}, {critical} critical")
if args.action in ("policies", "full") and args.policies:
result = assess_conditional_access(args.policies)
report["results"]["conditional_access"] = result
print(f"[+] Missing signals: {result['missing_signals']}")
if args.action in ("maturity", "full") and args.maturity_config:
with open(args.maturity_config) as f:
config = json.load(f)
result = assess_identity_maturity(config)
report["results"]["maturity"] = result
print(f"[+] Identity maturity: {result['overall_level']} (score: {result['overall_score']})")
if args.action in ("events", "full") and args.auth_events:
result = analyze_auth_events(args.auth_events)
report["results"]["events"] = result
print(f"[+] Auth events: {result['total_events']}, password-only: {result['password_only_rate']}%")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
process.py14.3 KB
#!/usr/bin/env python3
"""
Identity Verification Assessment Tool for Zero Trust
Analyzes identity configurations, evaluates MFA strength, assesses
conditional access policies, and generates identity maturity reports.
"""
import json
import csv
import sys
from datetime import datetime, timedelta
from pathlib import Path
from collections import defaultdict
from typing import Optional
def assess_mfa_strength(mfa_config: dict) -> dict:
"""Evaluate MFA configuration against zero trust requirements."""
findings = []
score = 100
methods = mfa_config.get("enabled_methods", [])
phishing_resistant = {"fido2", "webauthn", "certificate", "windows_hello", "platform_authenticator"}
phishable = {"sms", "voice", "email_otp"}
moderate = {"totp", "push_notification", "authenticator_app"}
enabled_phishing_resistant = set(methods) & phishing_resistant
enabled_phishable = set(methods) & phishable
if not enabled_phishing_resistant:
findings.append({
"severity": "critical",
"finding": "No phishing-resistant MFA methods enabled",
"recommendation": "Deploy FIDO2 security keys or platform authenticators",
"reference": "NIST SP 800-63B AAL3, CISA ZT Identity Pillar (Advanced)"
})
score -= 40
if enabled_phishable:
findings.append({
"severity": "high",
"finding": f"Phishable MFA methods still enabled: {', '.join(enabled_phishable)}",
"recommendation": "Disable SMS, voice, and email OTP methods",
"reference": "CISA Phishing-Resistant MFA Guidance"
})
score -= 20
if not mfa_config.get("enforced_for_all_users"):
findings.append({
"severity": "critical",
"finding": "MFA not enforced for all users",
"recommendation": "Enable MFA requirement for all user accounts",
"reference": "CISA ZT Identity Pillar (Initial)"
})
score -= 30
if not mfa_config.get("number_matching_enabled") and "push_notification" in methods:
findings.append({
"severity": "high",
"finding": "Push notification MFA without number matching",
"recommendation": "Enable number matching to prevent MFA fatigue attacks",
"reference": "Microsoft MFA fatigue defense guidance"
})
score -= 10
enrollment_rate = mfa_config.get("enrollment_rate_percent", 0)
if enrollment_rate < 95:
findings.append({
"severity": "warning",
"finding": f"MFA enrollment rate is {enrollment_rate}% (target: 95%+)",
"recommendation": "Launch enrollment campaign for remaining users",
"reference": "CISA ZT Identity Pillar (Advanced)"
})
score -= 10
return {
"mfa_score": max(score, 0),
"phishing_resistant_methods": list(enabled_phishing_resistant),
"phishable_methods": list(enabled_phishable),
"findings": findings,
"maturity_level": (
"optimal" if score >= 90 else
"advanced" if score >= 70 else
"initial" if score >= 50 else
"traditional"
)
}
def assess_conditional_access(policies: list) -> dict:
"""Evaluate conditional access policies for zero trust alignment."""
findings = []
coverage = {
"legacy_auth_blocked": False,
"mfa_required_all": False,
"device_compliance_required": False,
"location_restrictions": False,
"session_controls": False,
"risk_based_policies": False,
"privileged_access_secured": False,
}
for policy in policies:
conditions = policy.get("conditions", {})
grant_controls = policy.get("grant_controls", {})
session_controls = policy.get("session_controls", {})
if policy.get("blocks_legacy_auth"):
coverage["legacy_auth_blocked"] = True
if grant_controls.get("require_mfa") and conditions.get("users") == "all":
coverage["mfa_required_all"] = True
if grant_controls.get("require_compliant_device"):
coverage["device_compliance_required"] = True
if conditions.get("locations") and conditions["locations"].get("excluded_trusted"):
coverage["location_restrictions"] = True
if session_controls.get("sign_in_frequency") or session_controls.get("persistent_browser"):
coverage["session_controls"] = True
if conditions.get("user_risk") or conditions.get("sign_in_risk"):
coverage["risk_based_policies"] = True
if conditions.get("roles") and "privileged" in str(conditions.get("roles", [])).lower():
coverage["privileged_access_secured"] = True
if not coverage["legacy_auth_blocked"]:
findings.append({
"severity": "critical",
"finding": "Legacy authentication protocols not blocked",
"recommendation": "Create policy blocking basic auth, IMAP, POP3, SMTP AUTH",
})
if not coverage["mfa_required_all"]:
findings.append({
"severity": "critical",
"finding": "MFA not required for all users",
"recommendation": "Create policy requiring MFA for all user sign-ins",
})
if not coverage["device_compliance_required"]:
findings.append({
"severity": "high",
"finding": "Device compliance not required for access",
"recommendation": "Require managed and compliant devices for sensitive apps",
})
if not coverage["risk_based_policies"]:
findings.append({
"severity": "high",
"finding": "No risk-based conditional access policies",
"recommendation": "Enable user risk and sign-in risk based policies",
})
if not coverage["session_controls"]:
findings.append({
"severity": "warning",
"finding": "No session lifetime controls configured",
"recommendation": "Configure sign-in frequency and browser session persistence",
})
covered = sum(1 for v in coverage.values() if v)
total = len(coverage)
return {
"coverage": coverage,
"coverage_score": round((covered / total) * 100),
"policies_evaluated": len(policies),
"findings": findings,
}
def analyze_sign_in_logs(logs: list) -> dict:
"""Analyze sign-in logs for identity security insights."""
stats = {
"total_sign_ins": len(logs),
"successful": 0,
"failed": 0,
"mfa_challenged": 0,
"risk_events": [],
"locations": defaultdict(int),
"devices": defaultdict(int),
"applications": defaultdict(int),
"auth_methods": defaultdict(int),
"risky_sign_ins": [],
}
for log in logs:
if log.get("status") == "success":
stats["successful"] += 1
else:
stats["failed"] += 1
if log.get("mfa_required"):
stats["mfa_challenged"] += 1
location = log.get("location", {}).get("country", "unknown")
stats["locations"][location] += 1
device = log.get("device", {}).get("os", "unknown")
stats["devices"][device] += 1
app = log.get("application", "unknown")
stats["applications"][app] += 1
method = log.get("auth_method", "password")
stats["auth_methods"][method] += 1
risk_level = log.get("risk_level", "none")
if risk_level in ("medium", "high"):
stats["risky_sign_ins"].append({
"user": log.get("user", "unknown"),
"risk_level": risk_level,
"risk_detail": log.get("risk_detail", ""),
"location": location,
"timestamp": log.get("timestamp", ""),
"application": app,
})
stats["locations"] = dict(stats["locations"])
stats["devices"] = dict(stats["devices"])
stats["applications"] = dict(stats["applications"])
stats["auth_methods"] = dict(stats["auth_methods"])
stats["mfa_coverage_percent"] = round(
(stats["mfa_challenged"] / max(stats["total_sign_ins"], 1)) * 100, 1
)
return stats
def detect_impossible_travel(sign_ins: list, max_speed_kmh: int = 900) -> list:
"""Detect impossible travel scenarios from sign-in logs."""
alerts = []
user_logins = defaultdict(list)
for si in sign_ins:
user = si.get("user", "")
if user and si.get("location", {}).get("latitude"):
user_logins[user].append(si)
for user, logins in user_logins.items():
sorted_logins = sorted(logins, key=lambda x: x.get("timestamp", ""))
for i in range(1, len(sorted_logins)):
prev = sorted_logins[i - 1]
curr = sorted_logins[i]
lat1 = prev["location"].get("latitude", 0)
lon1 = prev["location"].get("longitude", 0)
lat2 = curr["location"].get("latitude", 0)
lon2 = curr["location"].get("longitude", 0)
import math
R = 6371
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = (math.sin(dlat / 2) ** 2 +
math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
math.sin(dlon / 2) ** 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance_km = R * c
try:
t1 = datetime.fromisoformat(prev.get("timestamp", "").replace("Z", "+00:00"))
t2 = datetime.fromisoformat(curr.get("timestamp", "").replace("Z", "+00:00"))
time_diff_hours = (t2 - t1).total_seconds() / 3600
except (ValueError, TypeError):
continue
if time_diff_hours > 0:
required_speed = distance_km / time_diff_hours
if required_speed > max_speed_kmh and distance_km > 100:
alerts.append({
"user": user,
"severity": "high",
"type": "impossible_travel",
"from_location": prev["location"],
"to_location": curr["location"],
"distance_km": round(distance_km),
"time_diff_hours": round(time_diff_hours, 2),
"required_speed_kmh": round(required_speed),
"from_time": prev.get("timestamp"),
"to_time": curr.get("timestamp"),
})
return alerts
def generate_identity_maturity_report(config: dict) -> dict:
"""Generate a comprehensive identity maturity assessment."""
report = {
"generated": datetime.now().isoformat(),
"overall_maturity": "traditional",
"pillars": {},
}
mfa_assessment = assess_mfa_strength(config.get("mfa", {}))
report["pillars"]["mfa"] = mfa_assessment
ca_assessment = assess_conditional_access(config.get("conditional_access_policies", []))
report["pillars"]["conditional_access"] = ca_assessment
governance = config.get("governance", {})
gov_score = 0
if governance.get("automated_provisioning"): gov_score += 25
if governance.get("access_reviews_enabled"): gov_score += 25
if governance.get("jit_access_enabled"): gov_score += 25
if governance.get("lifecycle_automation"): gov_score += 25
report["pillars"]["governance"] = {"score": gov_score}
monitoring = config.get("monitoring", {})
mon_score = 0
if monitoring.get("siem_integration"): mon_score += 25
if monitoring.get("ueba_enabled"): mon_score += 25
if monitoring.get("identity_threat_detection"): mon_score += 25
if monitoring.get("cae_enabled"): mon_score += 25
report["pillars"]["monitoring"] = {"score": mon_score}
avg_score = (
mfa_assessment["mfa_score"] +
ca_assessment["coverage_score"] +
gov_score +
mon_score
) / 4
report["overall_score"] = round(avg_score)
report["overall_maturity"] = (
"optimal" if avg_score >= 90 else
"advanced" if avg_score >= 70 else
"initial" if avg_score >= 50 else
"traditional"
)
return report
def main():
import argparse
parser = argparse.ArgumentParser(description="Identity Verification Assessment Tool")
parser.add_argument("--config", type=str, help="Path to identity configuration JSON")
parser.add_argument("--logs", type=str, help="Path to sign-in logs JSON")
parser.add_argument("--action", choices=["assess", "analyze-logs", "detect-travel", "report"],
default="report")
parser.add_argument("--output", type=str, default="identity_report.json")
args = parser.parse_args()
if args.action == "assess" and args.config:
with open(args.config) as f:
config = json.load(f)
mfa_result = assess_mfa_strength(config.get("mfa", {}))
ca_result = assess_conditional_access(config.get("conditional_access_policies", []))
result = {"mfa": mfa_result, "conditional_access": ca_result}
with open(args.output, "w") as f:
json.dump(result, f, indent=2)
print(f"MFA Score: {mfa_result['mfa_score']}, CA Coverage: {ca_result['coverage_score']}%")
elif args.action == "analyze-logs" and args.logs:
with open(args.logs) as f:
logs = json.load(f)
stats = analyze_sign_in_logs(logs)
with open(args.output, "w") as f:
json.dump(stats, f, indent=2)
print(f"Analyzed {stats['total_sign_ins']} sign-ins, {len(stats['risky_sign_ins'])} risky")
elif args.action == "detect-travel" and args.logs:
with open(args.logs) as f:
logs = json.load(f)
alerts = detect_impossible_travel(logs)
with open(args.output, "w") as f:
json.dump(alerts, f, indent=2)
print(f"Detected {len(alerts)} impossible travel events")
elif args.action == "report" and args.config:
with open(args.config) as f:
config = json.load(f)
report = generate_identity_maturity_report(config)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"Identity Maturity: {report['overall_maturity']} (Score: {report['overall_score']})")
else:
parser.print_help()
if args.output and Path(args.output).exists():
print(f"Output saved to {args.output}")
if __name__ == "__main__":
main()