phishing defense

Implementing Proofpoint Email Security Gateway

Deploy and configure Proofpoint Email Protection as a secure email gateway to detect and block phishing, malware, BEC, and spam before messages reach user inboxes.

anti-malwareanti-spambecemail-filteringemail-securityphishingproofpointsecure-email-gateway
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Proofpoint Email Protection is a cloud-native secure email gateway (SEG) that acts as a security checkpoint where all inbound and outbound mail traffic routes through the gateway before reaching user inboxes. It combines signature-based detection for known malware, machine learning algorithms for emerging threats, real-time threat intelligence feeds, URL rewriting with time-of-click sandboxing, and behavioral analysis for BEC detection. Proofpoint processes over 2.8 billion emails daily and blocks over 1 million extortion attempts per day.

When to Use

  • When deploying or configuring implementing proofpoint email security gateway 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

  • Proofpoint Email Protection license (PPS on-premises or Proofpoint on Demand cloud)
  • Administrative access to DNS management for MX record changes
  • Microsoft 365 or Google Workspace email environment
  • Understanding of mail flow architecture and SPF/DKIM/DMARC
  • Network firewall rules permitting Proofpoint IP ranges

Key Concepts

Deployment Models

  1. MX-Based Gateway (Traditional SEG): All mail routes through Proofpoint via MX record changes; intercepts threats before delivery
  2. API-Based Integration: Connects directly to Microsoft 365 or Google Workspace via API; no MX changes required; can be operational within 48 hours
  3. Hybrid Deployment: Combines gateway and API for layered protection

Core Detection Technologies

  • Impostor Classifier: ML model detecting BEC/impersonation with no malicious URLs or attachments
  • URL Defense: Rewrites URLs and performs real-time sandboxing at time of click
  • Attachment Defense: Sandboxes suspicious attachments in virtual environments
  • Nexus Threat Graph: Cross-customer threat intelligence correlation engine
  • Supplier Threat Detection: Identifies compromised vendor email accounts

Protection Layers

Layer Technology Threat Type
Connection IP reputation, rate limiting Spam botnets
Authentication SPF, DKIM, DMARC enforcement Spoofing
Content ML classifiers, NLP analysis BEC, phishing
URL Rewriting + time-of-click sandbox Credential theft
Attachment Static + dynamic sandboxing Malware, ransomware
Post-delivery TRAP (auto-retraction) Weaponized after delivery

Workflow

Step 1: Plan Mail Flow Architecture

  • Document current MX records and mail flow path
  • Identify all legitimate sending sources (marketing platforms, CRM, ticketing systems)
  • Map inbound connectors and transport rules in Microsoft 365 or Google Workspace
  • Plan IP allowlisting for Proofpoint egress IPs on receiving infrastructure
  • Configure SPF record to include Proofpoint: v=spf1 include:spf.protection.outlook.com include:spf-a.proofpoint.com -all

Step 2: Configure Proofpoint Policies

  • Create organizational units matching business structure
  • Define inbound mail policies: anti-spam, anti-virus, impostor detection
  • Configure Smart Search quarantine with end-user digest notifications
  • Set up Proofpoint Encryption for sensitive outbound messages
  • Enable Targeted Attack Protection (TAP) for URL and attachment sandboxing

Step 3: Deploy Email Authentication

  • Configure DKIM signing through Proofpoint for outbound messages
  • Set DMARC policy to monitor mode initially: v=DMARC1; p=none; rua=mailto:dmarc@company.com
  • Enable inbound DMARC enforcement to reject spoofed messages
  • Configure anti-spoofing rules for executive impersonation protection

Step 4: Enable Advanced Threat Protection

  • Activate URL Defense with rewriting enabled for all inbound messages
  • Configure Attachment Defense sandbox policies (safe attachment mode)
  • Enable Threat Response Auto-Pull (TRAP) for post-delivery remediation
  • Set up TAP Dashboard alerts for targeted attack campaigns
  • Configure Supplier Risk monitoring for vendor email compromise

Step 5: Migrate MX Records

  • Lower MX record TTL to 300 seconds 48 hours before cutover
  • Update MX records to point to Proofpoint: company-com.mail.protection.proofpoint.com
  • Configure connector restrictions in Microsoft 365 to accept mail only from Proofpoint IPs
  • Monitor mail flow through Proofpoint Message Trace for 48-72 hours
  • Verify no legitimate mail is being blocked or delayed

Step 6: Tune and Optimize

  • Review quarantine and false positive/negative rates weekly for first month
  • Adjust spam thresholds based on organizational tolerance
  • Add approved senders and safe lists for legitimate bulk mail
  • Configure data loss prevention (DLP) rules for outbound sensitive content
  • Enable email warning banners for external sender identification

Tools & Resources

  • Proofpoint TAP Dashboard: Real-time threat visibility and campaign tracking
  • Proofpoint TRAP: Automated post-delivery email retraction
  • Proofpoint SER (Spam/End-user Release): Self-service quarantine management
  • Proofpoint Closed-Loop Email Analysis (CLEAR): Phishing report button integration
  • MX Toolbox: DNS record verification and mail flow testing

Validation

  • All inbound email routes through Proofpoint (verify MX records and message headers)
  • TAP Dashboard shows threat detections and blocked campaigns
  • URL Defense rewrites links in test messages and sandboxes at click time
  • Attachment Defense detonates test malware samples in sandbox
  • TRAP successfully retracts test phishing message from inboxes post-delivery
  • False positive rate below 0.1% after initial tuning period
  • DMARC/SPF/DKIM authentication passes for all legitimate outbound mail
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.1 KB

API Reference: Proofpoint Email Security Gateway

Libraries Used

Library Purpose
requests HTTP client for Proofpoint TAP API v2
json Parse threat and message event data
os Read PROOFPOINT_SP and PROOFPOINT_SECRET credentials
datetime Build ISO-8601 time range queries

Installation

pip install requests

Authentication

Proofpoint TAP API uses HTTP Basic Auth with service principal and secret:

import requests
import os
from requests.auth import HTTPBasicAuth
 
PROOFPOINT_URL = "https://tap-api-v2.proofpoint.com"
auth = HTTPBasicAuth(
    os.environ["PROOFPOINT_SP"],       # Service Principal
    os.environ["PROOFPOINT_SECRET"],   # Secret
)

TAP API v2 Endpoints

Method Endpoint Description
GET /v2/siem/messages/blocked Messages blocked by Proofpoint
GET /v2/siem/messages/delivered Messages delivered (with threats)
GET /v2/siem/clicks/blocked Blocked URL clicks
GET /v2/siem/clicks/permitted Permitted URL clicks (with threats)
GET /v2/siem/all All events (messages + clicks)
GET /v2/siem/issues Campaign and threat issues
GET /v2/people/vap Very Attacked People report
GET /v2/forensics Threat forensics detail
POST /v2/quarantine/release Release message from quarantine
POST /v2/quarantine/delete Delete message from quarantine

Core Operations

Fetch Blocked Messages

from datetime import datetime, timedelta
 
def get_blocked_messages(hours_back=1):
    since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
    resp = requests.get(
        f"{PROOFPOINT_URL}/v2/siem/messages/blocked",
        auth=auth,
        params={
            "sinceTime": since,
            "format": "json",
        },
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json().get("messagesBlocked", [])

Fetch Permitted Clicks with Threats

def get_permitted_clicks(hours_back=24):
    since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
    resp = requests.get(
        f"{PROOFPOINT_URL}/v2/siem/clicks/permitted",
        auth=auth,
        params={"sinceTime": since, "format": "json"},
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json().get("clicksPermitted", [])

Get All SIEM Events

def get_all_events(hours_back=1):
    since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
    resp = requests.get(
        f"{PROOFPOINT_URL}/v2/siem/all",
        auth=auth,
        params={"sinceTime": since, "format": "json"},
        timeout=120,
    )
    resp.raise_for_status()
    data = resp.json()
    return {
        "messages_blocked": data.get("messagesBlocked", []),
        "messages_delivered": data.get("messagesDelivered", []),
        "clicks_blocked": data.get("clicksBlocked", []),
        "clicks_permitted": data.get("clicksPermitted", []),
    }

Get Very Attacked People (VAP)

def get_vap_report(days=30):
    resp = requests.get(
        f"{PROOFPOINT_URL}/v2/people/vap",
        auth=auth,
        params={"window": days, "size": 100},
        timeout=60,
    )
    resp.raise_for_status()
    return resp.json().get("users", [])

Extract Threat IOCs

def extract_iocs(events):
    iocs = {"urls": set(), "senders": set(), "subjects": set(), "sha256": set()}
    for msg in events.get("messages_blocked", []) + events.get("messages_delivered", []):
        iocs["senders"].add(msg.get("sender", ""))
        iocs["subjects"].add(msg.get("subject", ""))
        for threat in msg.get("threatsInfoMap", []):
            if threat.get("threatUrl"):
                iocs["urls"].add(threat["threatUrl"])
            if threat.get("sha256"):
                iocs["sha256"].add(threat["sha256"])
    return {k: list(v) for k, v in iocs.items()}

Query Parameters

Parameter Type Description
sinceTime ISO-8601 Start time (required, max 1 hour back for /all)
sinceSeconds int Seconds before now (alternative to sinceTime)
format string Response format: json (default) or syslog
threatType string Filter: url, attachment, messageText
threatStatus string Filter: active, cleared, falsePositive

Output Format

{
  "messagesBlocked": [
    {
      "GUID": "abc123-def456",
      "QID": "r1234567",
      "sender": "attacker@malicious.example.com",
      "recipient": ["user@company.com"],
      "subject": "Invoice #12345 Attached",
      "messageTime": "2025-01-15T10:30:00Z",
      "threatsInfoMap": [
        {
          "threat": "https://evil.example.com/payload",
          "threatType": "url",
          "threatStatus": "active",
          "classification": "phish",
          "sha256": "a1b2c3d4e5f6..."
        }
      ],
      "malwareScore": 100,
      "phishScore": 95,
      "spamScore": 0
    }
  ]
}
standards.md1.7 KB

Standards & References: Implementing Proofpoint Email Security Gateway

Industry Standards

  • NIST SP 800-177 Rev.1: Trustworthy Email - guidelines for email security deployment
  • RFC 7208: Sender Policy Framework (SPF) for authorizing use of domains in email
  • RFC 6376: DomainKeys Identified Mail (DKIM) Signatures
  • RFC 7489: Domain-based Message Authentication, Reporting & Conformance (DMARC)
  • CIS Controls v8 Control 9: Email and Web Browser Protections

MITRE ATT&CK References

  • T1566.001: Phishing: Spearphishing Attachment
  • T1566.002: Phishing: Spearphishing Link
  • T1566.003: Phishing: Spearphishing via Service
  • T1534: Internal Spearphishing
  • T1598: Phishing for Information
  • T1114.003: Email Collection: Email Forwarding Rule

Proofpoint-Specific References

  • Proofpoint Email Protection (PPS): On-premises protection platform
  • Proofpoint on Demand (PoD): Cloud-hosted email security service
  • Proofpoint TAP (Targeted Attack Protection): Advanced threat detection
  • Proofpoint TRAP (Threat Response Auto-Pull): Post-delivery remediation
  • Proofpoint Nexus Threat Graph: Cross-customer threat intelligence

Compliance Alignment

Framework Control Description
SOC 2 CC6.1 Logical and physical access controls
HIPAA 164.312(a)(1) Access control for ePHI
PCI DSS 4.0 5.2 Anti-malware solutions
NIST CSF 2.0 PR.DS-1 Data-at-rest and data-in-transit protection
ISO 27001 A.8.23 Web filtering

Email Security Gateway Market

  • Proofpoint processes 2.8+ billion messages daily (2024)
  • Gartner Magic Quadrant Leader for Email Security (2019-2024)
  • Over 50% of Fortune 100 companies use Proofpoint
workflows.md2.7 KB

Workflows: Implementing Proofpoint Email Security Gateway

Workflow 1: Inbound Mail Processing Pipeline

External sender sends email
  |
  v
[DNS MX lookup resolves to Proofpoint]
  |
  v
[Connection-level filtering]
  +-- IP reputation check (Proofpoint Nexus)
  +-- Rate limiting and connection throttling
  +-- REJECT if known-bad IP
  |
  v
[Authentication checks]
  +-- SPF validation
  +-- DKIM signature verification
  +-- DMARC policy evaluation
  +-- FAIL actions: quarantine or reject per policy
  |
  v
[Content analysis]
  +-- Anti-spam scoring (ML classifier)
  +-- Anti-virus scanning (multi-engine)
  +-- Impostor classifier (BEC detection)
  +-- NLP analysis for social engineering language
  |
  v
[URL Defense]
  +-- Extract all URLs from body and attachments
  +-- Rewrite URLs through Proofpoint proxy
  +-- Pre-delivery URL reputation check
  +-- BLOCK if known malicious
  |
  v
[Attachment Defense]
  +-- Static analysis (signatures, heuristics)
  +-- Dynamic sandbox detonation (if suspicious)
  +-- Wait for sandbox verdict (up to 7 minutes)
  +-- QUARANTINE if malicious
  |
  v
[Policy action]
  +-- DELIVER: Clean email to mailbox
  +-- TAG: Add warning banner for external/suspicious
  +-- QUARANTINE: Hold for admin/user review
  +-- REJECT: Block with NDR to sender

Workflow 2: Post-Delivery Threat Response (TRAP)

Threat intelligence update received
  |
  v
[TRAP scans delivered messages retroactively]
  +-- URL becomes malicious after delivery
  +-- New malware signature matches delivered attachment
  |
  v
[Auto-Pull action triggered]
  +-- Move message from user inbox to quarantine
  +-- Log retraction in TRAP dashboard
  +-- Notify SOC team of post-delivery threat
  |
  v
[SOC investigation]
  +-- Review TRAP alert and threat details
  +-- Check if user clicked URL before retraction
  +-- If clicked: initiate incident response
  +-- If not clicked: close as contained
  |
  v
[Update policies]
  +-- Add sender/domain to block list if needed
  +-- Create detection rule for similar campaigns
  +-- Update TAP Dashboard threat tracking

Workflow 3: Phishing Report and CLEAR Integration

User receives suspicious email
  |
  v
[User clicks "Report Phishing" button (Proofpoint CLEAR)]
  |
  v
[Email forwarded to Proofpoint analysis pipeline]
  +-- Automated classification (phishing/spam/clean)
  +-- URL and attachment analysis
  |
  v
[CLEAR verdict]
  +-- MALICIOUS: Auto-retract from all inboxes that received it
  +-- SPAM: Move to junk for all recipients
  +-- CLEAN: Return to inbox, thank reporter
  |
  v
[Metrics and feedback]
  +-- Track reporter accuracy rate
  +-- Update user risk score
  +-- Feed into security awareness metrics

Scripts 2

agent.py8.2 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Proofpoint email security gateway audit agent.

Audits Proofpoint TAP (Targeted Attack Protection) via the SIEM API
to retrieve blocked threats, clicked URLs, delivered messages, and
campaign attribution data for email security monitoring.
"""
import argparse
import json
import os
import sys
from datetime import datetime, timezone, timedelta

try:
    import requests
    from requests.auth import HTTPBasicAuth
except ImportError:
    print("[!] 'requests' required: pip install requests", file=sys.stderr)
    sys.exit(1)

PROOFPOINT_BASE = "https://tap-api-v2.proofpoint.com"


def get_pp_config():
    """Return Proofpoint TAP API credentials."""
    principal = os.environ.get("PROOFPOINT_PRINCIPAL", "")
    secret = os.environ.get("PROOFPOINT_SECRET", "")
    if not principal or not secret:
        print("[!] Set PROOFPOINT_PRINCIPAL and PROOFPOINT_SECRET env vars", file=sys.stderr)
        sys.exit(1)
    return principal, secret


def pp_api(endpoint, principal, secret, params=None):
    """Make authenticated Proofpoint TAP API call."""
    url = f"{PROOFPOINT_BASE}{endpoint}"
    resp = requests.get(url, auth=HTTPBasicAuth(principal, secret),
                        params=params, timeout=30)
    resp.raise_for_status()
    return resp.json()


def get_blocked_clicks(principal, secret, hours=24):
    """Get URLs that were blocked when clicked."""
    print(f"[*] Fetching blocked clicks (last {hours}h)...")
    since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
    data = pp_api("/v2/siem/clicks/blocked", principal, secret,
                  params={"sinceTime": since, "format": "json"})
    clicks = data.get("clicksBlocked", [])
    print(f"[+] {len(clicks)} blocked clicks")
    return clicks


def get_blocked_messages(principal, secret, hours=24):
    """Get messages that were blocked."""
    print(f"[*] Fetching blocked messages (last {hours}h)...")
    since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
    data = pp_api("/v2/siem/messages/blocked", principal, secret,
                  params={"sinceTime": since, "format": "json"})
    messages = data.get("messagesBlocked", [])
    print(f"[+] {len(messages)} blocked messages")
    return messages


def get_delivered_threats(principal, secret, hours=24):
    """Get threats that were delivered (missed by filters)."""
    print(f"[*] Fetching delivered threats (last {hours}h)...")
    since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
    data = pp_api("/v2/siem/messages/delivered", principal, secret,
                  params={"sinceTime": since, "format": "json"})
    messages = data.get("messagesDelivered", [])
    threats = [m for m in messages if m.get("threatsInfoMap")]
    print(f"[+] {len(messages)} delivered, {len(threats)} with threats")
    return threats


def get_permitted_clicks(principal, secret, hours=24):
    """Get URLs that were permitted when clicked (potential misses)."""
    print(f"[*] Fetching permitted clicks (last {hours}h)...")
    since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
    data = pp_api("/v2/siem/clicks/permitted", principal, secret,
                  params={"sinceTime": since, "format": "json"})
    clicks = data.get("clicksPermitted", [])
    print(f"[+] {len(clicks)} permitted clicks")
    return clicks


def analyze_threats(blocked_msgs, delivered_threats, blocked_clicks, permitted_clicks):
    """Analyze threat data for security insights."""
    findings = []

    # Delivered threats are highest priority
    for msg in delivered_threats:
        for threat_info in msg.get("threatsInfoMap", []):
            findings.append({
                "type": "delivered_threat",
                "severity": "CRITICAL",
                "threat_type": threat_info.get("threatType", ""),
                "classification": threat_info.get("classification", ""),
                "threat_url": threat_info.get("threat", "")[:100],
                "recipient": msg.get("recipient", [""])[0] if msg.get("recipient") else "",
                "sender": msg.get("sender", ""),
                "subject": msg.get("subject", "")[:80],
                "timestamp": msg.get("messageTime", ""),
            })

    # Summarize blocked activity
    threat_types = {}
    for msg in blocked_msgs:
        for t in msg.get("threatsInfoMap", []):
            tt = t.get("threatType", "unknown")
            threat_types[tt] = threat_types.get(tt, 0) + 1

    if threat_types:
        findings.append({
            "type": "blocked_summary",
            "severity": "INFO",
            "detail": f"Blocked threats by type: {json.dumps(threat_types)}",
            "total_blocked": len(blocked_msgs),
        })

    # Permitted clicks on potentially malicious URLs
    for click in permitted_clicks:
        if click.get("threatStatus") == "active":
            findings.append({
                "type": "permitted_malicious_click",
                "severity": "HIGH",
                "url": click.get("url", "")[:100],
                "user": click.get("recipient", [""])[0] if click.get("recipient") else "",
                "click_time": click.get("clickTime", ""),
            })

    return findings


def format_summary(findings, blocked_msgs, delivered, blocked_clicks, permitted_clicks):
    """Print email security summary."""
    print(f"\n{'='*60}")
    print(f"  Proofpoint Email Security Report")
    print(f"{'='*60}")
    print(f"  Blocked Messages  : {len(blocked_msgs)}")
    print(f"  Delivered Threats  : {len(delivered)}")
    print(f"  Blocked Clicks     : {len(blocked_clicks)}")
    print(f"  Permitted Clicks   : {len(permitted_clicks)}")
    print(f"  Security Findings  : {len(findings)}")

    critical = [f for f in findings if f["severity"] == "CRITICAL"]
    if critical:
        print(f"\n  CRITICAL - Threats That Bypassed Filters ({len(critical)}):")
        for f in critical[:10]:
            print(f"    {f.get('threat_type', 'N/A'):15s} | "
                  f"To: {f.get('recipient', 'N/A'):30s} | "
                  f"{f.get('subject', '')[:40]}")

    severity_counts = {}
    for f in findings:
        sev = f.get("severity", "INFO")
        severity_counts[sev] = severity_counts.get(sev, 0) + 1
    return severity_counts


def main():
    parser = argparse.ArgumentParser(description="Proofpoint email security audit agent")
    parser.add_argument("--principal", help="TAP API principal (or PROOFPOINT_PRINCIPAL env)")
    parser.add_argument("--secret", help="TAP API secret (or PROOFPOINT_SECRET env)")
    parser.add_argument("--hours", type=int, default=24, help="Hours to look back (default: 24)")
    parser.add_argument("--output", "-o", help="Output JSON report")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    if args.principal:
        os.environ["PROOFPOINT_PRINCIPAL"] = args.principal
    if args.secret:
        os.environ["PROOFPOINT_SECRET"] = args.secret

    principal, secret = get_pp_config()

    blocked_msgs = get_blocked_messages(principal, secret, args.hours)
    delivered = get_delivered_threats(principal, secret, args.hours)
    blocked_clicks = get_blocked_clicks(principal, secret, args.hours)
    permitted_clicks = get_permitted_clicks(principal, secret, args.hours)

    findings = analyze_threats(blocked_msgs, delivered, blocked_clicks, permitted_clicks)
    severity_counts = format_summary(findings, blocked_msgs, delivered, blocked_clicks, permitted_clicks)

    report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "Proofpoint TAP",
        "period_hours": args.hours,
        "blocked_messages": len(blocked_msgs),
        "delivered_threats": len(delivered),
        "findings": findings,
        "severity_counts": severity_counts,
        "risk_level": (
            "CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
            else "HIGH" if severity_counts.get("HIGH", 0) > 0
            else "LOW"
        ),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py11.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Proofpoint Email Security Gateway Configuration Validator

Validates MX records, SPF/DKIM/DMARC alignment, and Proofpoint
mail flow configuration for a given domain. Uses DNS lookups to
verify that email is routing through Proofpoint correctly.

Usage:
    python process.py check-mx --domain example.com
    python process.py check-auth --domain example.com
    python process.py validate-headers --eml-file message.eml
"""

import argparse
import json
import re
import sys
from dataclasses import dataclass, field, asdict
from typing import Optional

try:
    import dns.resolver
    HAS_DNS = True
except ImportError:
    HAS_DNS = False


@dataclass
class MXCheckResult:
    """MX record validation result."""
    domain: str = ""
    mx_records: list = field(default_factory=list)
    routes_through_proofpoint: bool = False
    proofpoint_mx: str = ""
    issues: list = field(default_factory=list)


@dataclass
class AuthCheckResult:
    """Email authentication check result."""
    domain: str = ""
    spf_record: str = ""
    spf_includes_proofpoint: bool = False
    dmarc_record: str = ""
    dmarc_policy: str = ""
    dkim_selector: str = ""
    issues: list = field(default_factory=list)


@dataclass
class HeaderAnalysis:
    """Email header forensic analysis."""
    from_address: str = ""
    return_path: str = ""
    received_chain: list = field(default_factory=list)
    spf_result: str = ""
    dkim_result: str = ""
    dmarc_result: str = ""
    proofpoint_headers: list = field(default_factory=list)
    x_proofpoint_spam_details: str = ""
    x_proofpoint_virus_version: str = ""
    passed_through_proofpoint: bool = False
    issues: list = field(default_factory=list)


PROOFPOINT_MX_PATTERNS = [
    r'\.pphosted\.com$',
    r'\.proofpoint\.com$',
    r'mail\.protection\.proofpoint\.com$',
]

PROOFPOINT_SPF_INCLUDES = [
    'spf-a.proofpoint.com',
    'spf-b.proofpoint.com',
    'spf.proofpoint.com',
    'pphosted.com',
]

PROOFPOINT_HEADER_MARKERS = [
    'X-Proofpoint-Spam-Details',
    'X-Proofpoint-Virus-Version',
    'X-Proofpoint-GUID',
    'X-Proofpoint-ORIG-GUID',
]


def check_mx_records(domain: str) -> MXCheckResult:
    """Check if domain MX records route through Proofpoint."""
    result = MXCheckResult(domain=domain)

    if not HAS_DNS:
        result.issues.append("dnspython not installed. Install with: pip install dnspython")
        return result

    try:
        answers = dns.resolver.resolve(domain, 'MX')
        for rdata in sorted(answers, key=lambda r: r.preference):
            mx_host = str(rdata.exchange).rstrip('.')
            result.mx_records.append({
                "priority": rdata.preference,
                "host": mx_host
            })
            for pattern in PROOFPOINT_MX_PATTERNS:
                if re.search(pattern, mx_host, re.IGNORECASE):
                    result.routes_through_proofpoint = True
                    result.proofpoint_mx = mx_host
                    break
    except dns.resolver.NXDOMAIN:
        result.issues.append(f"Domain {domain} does not exist")
    except dns.resolver.NoAnswer:
        result.issues.append(f"No MX records found for {domain}")
    except Exception as e:
        result.issues.append(f"DNS query failed: {str(e)}")

    if not result.routes_through_proofpoint and result.mx_records:
        result.issues.append(
            "MX records do not point to Proofpoint. "
            "Expected pattern: *.pphosted.com or *.proofpoint.com"
        )

    return result


def check_authentication(domain: str) -> AuthCheckResult:
    """Check SPF, DKIM, and DMARC records for Proofpoint alignment."""
    result = AuthCheckResult(domain=domain)

    if not HAS_DNS:
        result.issues.append("dnspython not installed. Install with: pip install dnspython")
        return result

    # Check SPF
    try:
        answers = dns.resolver.resolve(domain, 'TXT')
        for rdata in answers:
            txt = str(rdata).strip('"')
            if txt.startswith('v=spf1'):
                result.spf_record = txt
                for include in PROOFPOINT_SPF_INCLUDES:
                    if include in txt:
                        result.spf_includes_proofpoint = True
                        break
                break
    except Exception:
        result.issues.append("Could not retrieve SPF record")

    if result.spf_record and not result.spf_includes_proofpoint:
        result.issues.append(
            "SPF record does not include Proofpoint. "
            "Add: include:spf-a.proofpoint.com"
        )

    # Check DMARC
    try:
        dmarc_domain = f"_dmarc.{domain}"
        answers = dns.resolver.resolve(dmarc_domain, 'TXT')
        for rdata in answers:
            txt = str(rdata).strip('"')
            if txt.startswith('v=DMARC1'):
                result.dmarc_record = txt
                policy_match = re.search(r'p=(\w+)', txt)
                if policy_match:
                    result.dmarc_policy = policy_match.group(1)
                break
    except Exception:
        result.issues.append("No DMARC record found")

    if result.dmarc_policy == "none":
        result.issues.append(
            "DMARC policy is set to 'none' (monitoring only). "
            "Plan rollout to 'quarantine' then 'reject'"
        )

    # Check Proofpoint DKIM selector
    try:
        dkim_domain = f"proofpoint._domainkey.{domain}"
        answers = dns.resolver.resolve(dkim_domain, 'TXT')
        for rdata in answers:
            result.dkim_selector = "proofpoint"
            break
    except Exception:
        pass

    return result


def analyze_headers(eml_content: str) -> HeaderAnalysis:
    """Analyze email headers for Proofpoint routing and authentication."""
    analysis = HeaderAnalysis()

    # Extract From header
    from_match = re.search(r'^From:\s*(.+)$', eml_content, re.MULTILINE | re.IGNORECASE)
    if from_match:
        analysis.from_address = from_match.group(1).strip()

    # Extract Return-Path
    rp_match = re.search(r'^Return-Path:\s*(.+)$', eml_content, re.MULTILINE | re.IGNORECASE)
    if rp_match:
        analysis.return_path = rp_match.group(1).strip()

    # Extract Received chain
    received_headers = re.findall(
        r'^Received:\s*(.*?)(?=\n\S|\nReceived:|\n\n)',
        eml_content, re.MULTILINE | re.DOTALL | re.IGNORECASE
    )
    for hdr in received_headers:
        clean = ' '.join(hdr.split())
        analysis.received_chain.append(clean)
        if any(p.replace(r'$', '').replace(r'\.', '.') in clean.lower()
               for p in ['pphosted.com', 'proofpoint.com']):
            analysis.passed_through_proofpoint = True

    # Extract Authentication-Results
    auth_match = re.search(
        r'^Authentication-Results:\s*(.*?)(?=\n\S)',
        eml_content, re.MULTILINE | re.DOTALL | re.IGNORECASE
    )
    if auth_match:
        auth_text = auth_match.group(1)
        spf_match = re.search(r'spf=(\w+)', auth_text)
        if spf_match:
            analysis.spf_result = spf_match.group(1)
        dkim_match = re.search(r'dkim=(\w+)', auth_text)
        if dkim_match:
            analysis.dkim_result = dkim_match.group(1)
        dmarc_match = re.search(r'dmarc=(\w+)', auth_text)
        if dmarc_match:
            analysis.dmarc_result = dmarc_match.group(1)

    # Check for Proofpoint-specific headers
    for marker in PROOFPOINT_HEADER_MARKERS:
        marker_match = re.search(
            rf'^{re.escape(marker)}:\s*(.+)$',
            eml_content, re.MULTILINE | re.IGNORECASE
        )
        if marker_match:
            analysis.proofpoint_headers.append({
                "header": marker,
                "value": marker_match.group(1).strip()
            })
            if marker == 'X-Proofpoint-Spam-Details':
                analysis.x_proofpoint_spam_details = marker_match.group(1).strip()
            elif marker == 'X-Proofpoint-Virus-Version':
                analysis.x_proofpoint_virus_version = marker_match.group(1).strip()

    if not analysis.passed_through_proofpoint and not analysis.proofpoint_headers:
        analysis.issues.append("Email does not appear to have routed through Proofpoint")

    if analysis.spf_result and analysis.spf_result != 'pass':
        analysis.issues.append(f"SPF check failed: {analysis.spf_result}")
    if analysis.dkim_result and analysis.dkim_result != 'pass':
        analysis.issues.append(f"DKIM check failed: {analysis.dkim_result}")
    if analysis.dmarc_result and analysis.dmarc_result != 'pass':
        analysis.issues.append(f"DMARC check failed: {analysis.dmarc_result}")

    return analysis


def format_report(title: str, data: dict) -> str:
    """Format check results as a readable report."""
    lines = []
    lines.append("=" * 60)
    lines.append(f"  {title}")
    lines.append("=" * 60)

    for key, value in data.items():
        if key == 'issues':
            if value:
                lines.append(f"\n  [ISSUES]")
                for i, issue in enumerate(value, 1):
                    lines.append(f"    {i}. {issue}")
        elif isinstance(value, list):
            lines.append(f"\n  {key}:")
            for item in value:
                if isinstance(item, dict):
                    lines.append(f"    - {json.dumps(item)}")
                else:
                    lines.append(f"    - {item}")
        elif isinstance(value, bool):
            status = "YES" if value else "NO"
            lines.append(f"  {key}: {status}")
        else:
            lines.append(f"  {key}: {value}")

    lines.append("=" * 60)
    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(description="Proofpoint Email Gateway Validator")
    subparsers = parser.add_subparsers(dest="command")

    mx_parser = subparsers.add_parser("check-mx", help="Check MX records for Proofpoint routing")
    mx_parser.add_argument("--domain", required=True, help="Domain to check")

    auth_parser = subparsers.add_parser("check-auth", help="Check email authentication records")
    auth_parser.add_argument("--domain", required=True, help="Domain to check")

    hdr_parser = subparsers.add_parser("validate-headers", help="Analyze email headers")
    hdr_parser.add_argument("--eml-file", required=True, help="Path to .eml file")

    parser.add_argument("--json", action="store_true", help="Output as JSON")

    args = parser.parse_args()

    if args.command == "check-mx":
        result = check_mx_records(args.domain)
        if args.json:
            print(json.dumps(asdict(result), indent=2))
        else:
            print(format_report("PROOFPOINT MX RECORD CHECK", asdict(result)))

    elif args.command == "check-auth":
        result = check_authentication(args.domain)
        if args.json:
            print(json.dumps(asdict(result), indent=2))
        else:
            print(format_report("EMAIL AUTHENTICATION CHECK", asdict(result)))

    elif args.command == "validate-headers":
        with open(args.eml_file, 'r', errors='replace') as f:
            content = f.read()
        result = analyze_headers(content)
        if args.json:
            print(json.dumps(asdict(result), indent=2))
        else:
            print(format_report("EMAIL HEADER ANALYSIS", asdict(result)))

    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.7 KB
Keep exploring