zero trust architecture

Implementing Identity Verification for Zero Trust

Implement continuous identity verification for zero trust using phishing-resistant MFA (FIDO2/WebAuthn), risk-based conditional access, and identity governance aligned with the CISA Zero Trust Maturity Model.

authenticationidentityidentity-verificationmfazero-trust
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

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

  1. Primary IdP: Azure AD / Okta / Ping Identity for centralized identity management
  2. FIDO2 Authenticators: Hardware security keys (YubiKey) or platform authenticators (Windows Hello, Touch ID)
  3. Risk Engine: Adaptive access using identity threat detection (Microsoft Entra ID Protection, Okta ThreatInsight)
  4. Identity Governance: Lifecycle management, access reviews, just-in-time provisioning
  5. 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

  1. 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
  2. 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
  3. 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

  1. 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
  2. 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

  1. 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
  2. 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

  1. 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
  2. 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
Source materials

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

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
Display-only source. This catalog never executes bundled scripts.
#!/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
Display-only source. This catalog never executes bundled scripts.
#!/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()

Assets 1

template.mdtext/markdown · 3.1 KB
Keep exploring