Files

225 lines
7.3 KiB
Python

#!/usr/bin/env python3
"""
Google Workspace Phishing Protection Auditor
Audits Google Workspace Gmail safety settings configuration
and generates compliance recommendations.
Usage:
python process.py audit --config-file gws_config.json
python process.py check-auth --domain example.com
"""
import argparse
import json
import sys
from dataclasses import dataclass, field, asdict
try:
import dns.resolver
HAS_DNS = True
except ImportError:
HAS_DNS = False
@dataclass
class GWSSecurityAudit:
"""Google Workspace security configuration audit."""
domain: str = ""
spoofing_protection: bool = False
employee_name_spoofing: bool = False
enhanced_predelivery: bool = False
attachment_protection: dict = field(default_factory=dict)
enhanced_safe_browsing: bool = False
security_sandbox: bool = False
app_enrolled_users: int = 0
spf_configured: bool = False
dkim_configured: bool = False
dmarc_configured: bool = False
dmarc_policy: str = ""
score: int = 0
max_score: int = 100
findings: list = field(default_factory=list)
recommendations: list = field(default_factory=list)
def audit_gws_config(config: dict) -> GWSSecurityAudit:
"""Audit Google Workspace Gmail safety configuration."""
audit = GWSSecurityAudit()
audit.domain = config.get("domain", "")
safety = config.get("safety_settings", {})
# Spoofing protection
audit.spoofing_protection = safety.get("domain_spoofing_protection", False)
if audit.spoofing_protection:
audit.score += 15
else:
audit.findings.append("Domain spoofing protection not enabled")
audit.recommendations.append(
"Enable 'Protect against domain spoofing based on similar domain names'"
)
audit.employee_name_spoofing = safety.get("employee_name_spoofing", False)
if audit.employee_name_spoofing:
audit.score += 10
else:
audit.findings.append("Employee name spoofing protection not enabled")
audit.recommendations.append(
"Enable 'Protect against spoofing of employee names'"
)
# Pre-delivery scanning
audit.enhanced_predelivery = safety.get("enhanced_predelivery_scanning", False)
if audit.enhanced_predelivery:
audit.score += 15
else:
audit.findings.append("Enhanced pre-delivery scanning not enabled")
# Attachment protection
att = safety.get("attachment_protection", {})
audit.attachment_protection = att
att_score = 0
if att.get("encrypted_attachments", False):
att_score += 5
if att.get("script_attachments", False):
att_score += 5
if att.get("anomalous_types", False):
att_score += 5
audit.score += att_score
if att_score < 15:
audit.findings.append("Not all attachment protection options enabled")
# Enhanced Safe Browsing
audit.enhanced_safe_browsing = safety.get("enhanced_safe_browsing", False)
if audit.enhanced_safe_browsing:
audit.score += 10
else:
audit.findings.append("Enhanced Safe Browsing not enabled (off by default)")
audit.recommendations.append(
"Enable Enhanced Safe Browsing in Admin Console > Security"
)
# Security Sandbox
audit.security_sandbox = safety.get("security_sandbox", False)
if audit.security_sandbox:
audit.score += 10
else:
audit.findings.append("Gmail Security Sandbox not enabled (requires Enterprise license)")
# Advanced Protection Program
audit.app_enrolled_users = config.get("app_enrolled_users", 0)
if audit.app_enrolled_users > 0:
audit.score += 10
else:
audit.recommendations.append(
"Enroll super admins and executives in Advanced Protection Program"
)
# Authentication
auth = config.get("authentication", {})
audit.spf_configured = auth.get("spf", False)
audit.dkim_configured = auth.get("dkim", False)
audit.dmarc_configured = auth.get("dmarc", False)
audit.dmarc_policy = auth.get("dmarc_policy", "none")
if audit.spf_configured:
audit.score += 5
if audit.dkim_configured:
audit.score += 5
if audit.dmarc_configured:
audit.score += 5
if audit.dmarc_policy == "reject":
audit.score += 10
elif audit.dmarc_policy == "quarantine":
audit.score += 5
return audit
def check_google_auth(domain: str) -> dict:
"""Check SPF, DKIM, DMARC for Google Workspace domain."""
result = {"domain": domain, "spf": False, "dkim": False, "dmarc": False, "issues": []}
if not HAS_DNS:
result["issues"].append("dnspython not installed")
return result
# SPF
try:
answers = dns.resolver.resolve(domain, 'TXT')
for rdata in answers:
txt = str(rdata).strip('"')
if 'v=spf1' in txt and '_spf.google.com' in txt:
result["spf"] = True
break
if not result["spf"]:
result["issues"].append("SPF does not include _spf.google.com")
except Exception:
result["issues"].append("No SPF record found")
# DKIM (Google default selector)
try:
dns.resolver.resolve(f"google._domainkey.{domain}", 'TXT')
result["dkim"] = True
except Exception:
result["issues"].append("DKIM not configured for 'google' selector")
# DMARC
try:
answers = dns.resolver.resolve(f"_dmarc.{domain}", 'TXT')
for rdata in answers:
txt = str(rdata).strip('"')
if 'v=DMARC1' in txt:
result["dmarc"] = True
import re
policy = re.search(r'p=(\w+)', txt)
result["dmarc_policy"] = policy.group(1) if policy else "unknown"
break
except Exception:
result["issues"].append("No DMARC record found")
return result
def main():
parser = argparse.ArgumentParser(description="Google Workspace Phishing Protection Auditor")
subparsers = parser.add_subparsers(dest="command")
audit_parser = subparsers.add_parser("audit", help="Audit GWS security config")
audit_parser.add_argument("--config-file", required=True)
auth_parser = subparsers.add_parser("check-auth", help="Check email authentication")
auth_parser.add_argument("--domain", required=True)
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
if args.command == "audit":
with open(args.config_file) as f:
config = json.load(f)
result = audit_gws_config(config)
if args.json:
print(json.dumps(asdict(result), indent=2))
else:
print(f"Security Score: {result.score}/{result.max_score}")
if result.findings:
print(f"\nFindings ({len(result.findings)}):")
for i, f_item in enumerate(result.findings, 1):
print(f" {i}. {f_item}")
if result.recommendations:
print(f"\nRecommendations ({len(result.recommendations)}):")
for i, rec in enumerate(result.recommendations, 1):
print(f" {i}. {rec}")
elif args.command == "check-auth":
result = check_google_auth(args.domain)
print(json.dumps(result, indent=2))
else:
parser.print_help()
if __name__ == "__main__":
main()