npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
API Security Posture Management (API-SPM) provides continuous visibility into an organization's API attack surface by automatically discovering, classifying, and risk-scoring all APIs including internal, external, partner, and shadow endpoints. Unlike point-in-time testing tools, API-SPM operates continuously to detect configuration drift, policy violations, missing security controls, sensitive data exposure, and compliance gaps. It aggregates findings from DAST, SAST, SCA, and runtime monitoring tools to provide a unified view of API risk posture across the organization.
When to Use
- When deploying or configuring implementing api security posture management 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
- API gateway with traffic logging (Kong, AWS API Gateway, Apigee, Envoy)
- OpenAPI specifications for documented APIs
- SIEM or log aggregation platform (Splunk, Elastic)
- CI/CD pipeline access for shift-left integration
- Cloud provider APIs for infrastructure discovery
- Python 3.8+ for custom posture assessment tooling
Core Components
1. API Discovery and Inventory
#!/usr/bin/env python3
"""API Security Posture Management Engine
Continuously discovers, classifies, and risk-scores APIs
to maintain a comprehensive security posture inventory.
"""
import json
import re
import hashlib
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
from enum import Enum
class APIClassification(Enum):
EXTERNAL = "external"
INTERNAL = "internal"
PARTNER = "partner"
SHADOW = "shadow"
DEPRECATED = "deprecated"
class RiskLevel(Enum):
CRITICAL = 4
HIGH = 3
MEDIUM = 2
LOW = 1
INFO = 0
@dataclass
class SecurityControl:
name: str
present: bool
required: bool
severity: RiskLevel
details: str = ""
@dataclass
class APIEndpoint:
api_id: str
method: str
path: str
service_name: str
classification: APIClassification
owner: Optional[str] = None
version: Optional[str] = None
first_discovered: str = ""
last_seen: str = ""
documented: bool = False
security_controls: List[SecurityControl] = field(default_factory=list)
risk_score: float = 0.0
sensitive_data_types: Set[str] = field(default_factory=set)
compliance_tags: Set[str] = field(default_factory=set)
traffic_volume_daily: int = 0
class APIPostureManager:
SENSITIVE_PATTERNS = {
"ssn": re.compile(r'\b\d{3}-\d{2}-\d{4}\b'),
"credit_card": re.compile(r'\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b'),
"email": re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'),
"api_key": re.compile(r'\b[A-Za-z0-9]{32,}\b'),
"jwt": re.compile(r'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'),
"phone": re.compile(r'\b\+?1?\d{10,15}\b'),
}
def __init__(self):
self.inventory: Dict[str, APIEndpoint] = {}
self.policy_rules: List[dict] = []
def generate_api_id(self, method: str, path: str, service: str) -> str:
raw = f"{service}:{method}:{path}"
return hashlib.sha256(raw.encode()).hexdigest()[:16]
def register_api(self, method: str, path: str, service_name: str,
classification: APIClassification,
documented: bool = False, owner: str = None) -> APIEndpoint:
api_id = self.generate_api_id(method, path, service_name)
now = datetime.now().isoformat()
if api_id in self.inventory:
endpoint = self.inventory[api_id]
endpoint.last_seen = now
return endpoint
endpoint = APIEndpoint(
api_id=api_id,
method=method,
path=path,
service_name=service_name,
classification=classification,
owner=owner,
first_discovered=now,
last_seen=now,
documented=documented
)
self.inventory[api_id] = endpoint
return endpoint
def assess_security_controls(self, endpoint: APIEndpoint,
traffic_sample: dict) -> List[SecurityControl]:
"""Evaluate security controls present on an API endpoint."""
controls = []
# Authentication check
has_auth = any(h in traffic_sample.get('request_headers', {})
for h in ['Authorization', 'X-API-Key', 'Cookie'])
controls.append(SecurityControl(
name="authentication",
present=has_auth,
required=True,
severity=RiskLevel.CRITICAL,
details="No authentication mechanism detected" if not has_auth else "Authentication present"
))
# TLS/HTTPS check
is_https = traffic_sample.get('scheme', '').lower() == 'https'
controls.append(SecurityControl(
name="transport_encryption",
present=is_https,
required=True,
severity=RiskLevel.CRITICAL,
details="API accessible over HTTP without TLS" if not is_https else "HTTPS enforced"
))
# Rate limiting check
has_rate_limit = any(h.startswith('X-RateLimit') or h == 'Retry-After'
for h in traffic_sample.get('response_headers', {}).keys())
controls.append(SecurityControl(
name="rate_limiting",
present=has_rate_limit,
required=True,
severity=RiskLevel.HIGH,
details="No rate limiting headers detected" if not has_rate_limit else "Rate limiting active"
))
# CORS policy check
cors_origin = traffic_sample.get('response_headers', {}).get('Access-Control-Allow-Origin', '')
has_strict_cors = cors_origin and cors_origin != '*'
controls.append(SecurityControl(
name="cors_policy",
present=has_strict_cors,
required=endpoint.classification == APIClassification.EXTERNAL,
severity=RiskLevel.HIGH if cors_origin == '*' else RiskLevel.MEDIUM,
details=f"CORS origin: {cors_origin}" if cors_origin else "No CORS headers"
))
# Security headers
sec_headers = traffic_sample.get('response_headers', {})
required_headers = {
'X-Content-Type-Options': 'nosniff',
'Strict-Transport-Security': None,
'X-Frame-Options': None,
'Cache-Control': 'no-store',
}
missing = [h for h in required_headers if h not in sec_headers]
controls.append(SecurityControl(
name="security_headers",
present=len(missing) == 0,
required=True,
severity=RiskLevel.MEDIUM,
details=f"Missing headers: {', '.join(missing)}" if missing else "All security headers present"
))
# Input validation (check for schema validation errors in logs)
has_validation = traffic_sample.get('has_schema_validation', False)
controls.append(SecurityControl(
name="input_validation",
present=has_validation,
required=True,
severity=RiskLevel.HIGH,
details="No schema validation detected" if not has_validation else "Input validation active"
))
endpoint.security_controls = controls
return controls
def calculate_risk_score(self, endpoint: APIEndpoint) -> float:
"""Calculate a composite risk score (0-100) for an API endpoint."""
score = 0.0
max_score = 0.0
# Security controls scoring
for control in endpoint.security_controls:
weight = control.severity.value * 5
max_score += weight
if not control.present and control.required:
score += weight
# Classification risk multiplier
classification_weights = {
APIClassification.EXTERNAL: 1.5,
APIClassification.PARTNER: 1.3,
APIClassification.SHADOW: 2.0,
APIClassification.DEPRECATED: 1.8,
APIClassification.INTERNAL: 1.0,
}
multiplier = classification_weights.get(endpoint.classification, 1.0)
# Documentation penalty
if not endpoint.documented:
score += 10
# Sensitive data penalty
score += len(endpoint.sensitive_data_types) * 5
# Normalize to 0-100
if max_score > 0:
normalized = min(100, (score / max_score) * 100 * multiplier)
else:
normalized = 0
endpoint.risk_score = round(normalized, 1)
return endpoint.risk_score
def generate_posture_report(self) -> dict:
"""Generate organization-wide API security posture report."""
total = len(self.inventory)
if total == 0:
return {"error": "No APIs in inventory"}
risk_distribution = {level.name: 0 for level in RiskLevel}
classification_counts = {c.value: 0 for c in APIClassification}
undocumented = 0
missing_auth = 0
missing_tls = 0
for endpoint in self.inventory.values():
self.calculate_risk_score(endpoint)
if endpoint.risk_score >= 75:
risk_distribution["CRITICAL"] += 1
elif endpoint.risk_score >= 50:
risk_distribution["HIGH"] += 1
elif endpoint.risk_score >= 25:
risk_distribution["MEDIUM"] += 1
else:
risk_distribution["LOW"] += 1
classification_counts[endpoint.classification.value] += 1
if not endpoint.documented:
undocumented += 1
for control in endpoint.security_controls:
if control.name == "authentication" and not control.present:
missing_auth += 1
if control.name == "transport_encryption" and not control.present:
missing_tls += 1
avg_risk = sum(e.risk_score for e in self.inventory.values()) / total
return {
"report_date": datetime.now().isoformat(),
"total_apis": total,
"average_risk_score": round(avg_risk, 1),
"risk_distribution": risk_distribution,
"classification": classification_counts,
"undocumented_apis": undocumented,
"missing_authentication": missing_auth,
"missing_tls": missing_tls,
"top_risks": sorted(
[{"api_id": e.api_id, "method": e.method, "path": e.path,
"service": e.service_name, "risk_score": e.risk_score,
"classification": e.classification.value}
for e in self.inventory.values()],
key=lambda x: x["risk_score"],
reverse=True
)[:20]
}2. Policy Enforcement
Define and enforce security policies across all APIs:
# api-security-policies.yaml
policies:
- name: require-authentication
description: All external APIs must require authentication
scope:
classification: [external, partner]
rule:
control: authentication
required: true
severity: critical
remediation: "Add OAuth2, API key, or JWT authentication"
- name: enforce-tls
description: All APIs must use HTTPS
scope:
classification: [external, internal, partner]
rule:
control: transport_encryption
required: true
severity: critical
remediation: "Configure TLS certificates and redirect HTTP to HTTPS"
- name: require-rate-limiting
description: External APIs must implement rate limiting
scope:
classification: [external]
rule:
control: rate_limiting
required: true
severity: high
remediation: "Configure rate limiting at API gateway level"
- name: no-wildcard-cors
description: APIs must not use wildcard CORS origins
scope:
classification: [external]
rule:
control: cors_policy
condition: "origin != '*'"
severity: high
remediation: "Specify explicit allowed origins in CORS configuration"
- name: documentation-required
description: All APIs must have OpenAPI documentation
scope:
classification: [external, partner]
rule:
documented: true
severity: medium
remediation: "Create and publish OpenAPI specification"
- name: deprecation-sunset
description: Deprecated APIs must have sunset headers
scope:
classification: [deprecated]
rule:
header_present: "Sunset"
severity: medium
remediation: "Add Sunset header with planned removal date"Continuous Monitoring Dashboard Metrics
| Metric | Description | Target |
|---|---|---|
| API Discovery Coverage | % of APIs with documentation | > 95% |
| Average Risk Score | Mean risk score across all APIs | < 25 |
| Critical Findings | Number of critical-risk APIs | 0 |
| Shadow API Count | Undocumented/unmanaged APIs | 0 |
| Authentication Coverage | % of APIs with auth controls | 100% |
| TLS Coverage | % of APIs using HTTPS | 100% |
| Policy Compliance | % of APIs meeting all policies | > 90% |
| Mean Time to Remediate | Average days to fix findings | < 7 days |
References
- OX Security ASPM Guide 2025: https://www.ox.security/blog/application-security-posture-management-aspm/
- IBM ASPM Overview: https://www.ibm.com/think/topics/aspm
- StackHawk Best ASPM Tools: https://www.stackhawk.com/blog/best-aspm-tools/
- AppSentinels API Security Posture Management: https://appsentinels.ai/blog/api-security-posture-management-from-reactive-protection-to-continuous-governance/
- Palo Alto Networks ASPM: https://www.paloaltonetworks.com/cyberpedia/aspm-application-security-posture-management
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md1.4 KB
API Reference: Implementing API Security Posture Management
API Discovery from Traffic
import re
# Normalize paths: /users/123 -> /users/{id}
normalized = re.sub(r"/\d+", "/{id}", path)
normalized = re.sub(r"/[0-9a-f-]{8,}", "/{id}", normalized)API Sensitivity Classification
| Category | Patterns | Sensitivity |
|---|---|---|
| PII | /users, /profile, /account |
HIGH |
| Financial | /payments, /billing |
HIGH |
| Auth | /login, /token, /oauth |
HIGH |
| Admin | /admin, /config |
HIGH |
| Health | /health, /status |
LOW |
Risk Scoring Model
| Factor | Points | Description |
|---|---|---|
| High sensitivity data | +30 | PII, financial, auth |
| High error rate (>10%) | +20 | Possible abuse |
| State-changing methods | +10 | PUT, DELETE, PATCH |
| High consumer count | +10 | Large attack surface |
| Auth endpoint | +15 | Credential target |
42Crunch API Audit
# CI/CD integration
curl -X POST https://platform.42crunch.com/api/v1/apis \
-H "X-API-KEY: $API_KEY" \
-F "file=@openapi.yaml"Salt Security API
import requests
headers = {"Authorization": "Bearer <token>"}
# Discover shadow APIs
resp = requests.get("https://api.salt.security/v1/apis", headers=headers)References
- OWASP API Security Top 10: https://owasp.org/API-Security/
- 42Crunch: https://42crunch.com/
- Salt Security: https://salt.security/
Scripts 1
agent.py6.1 KB
#!/usr/bin/env python3
"""Agent for API Security Posture Management - discovery, classification, and risk scoring."""
import json
import argparse
import re
from datetime import datetime
from collections import Counter, defaultdict
def discover_apis_from_traffic(log_path):
"""Discover APIs from network traffic logs."""
apis = defaultdict(lambda: {"methods": set(), "consumers": set(), "count": 0,
"status_codes": Counter()})
with open(log_path) as f:
for line in f:
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
path = entry.get("path", entry.get("endpoint", ""))
method = entry.get("method", entry.get("http_method", ""))
ip = entry.get("client_ip", "")
status = entry.get("status_code", entry.get("status", 200))
normalized = re.sub(r"/[0-9a-f-]{8,}", "/{id}", path)
normalized = re.sub(r"/\d+", "/{id}", normalized)
apis[normalized]["methods"].add(method)
apis[normalized]["consumers"].add(ip)
apis[normalized]["count"] += 1
apis[normalized]["status_codes"][int(status)] += 1
result = []
for path, info in sorted(apis.items(), key=lambda x: -x[1]["count"]):
result.append({
"path": path,
"methods": sorted(info["methods"]),
"unique_consumers": len(info["consumers"]),
"total_requests": info["count"],
"error_rate": round(
sum(v for k, v in info["status_codes"].items() if k >= 400) / info["count"], 3),
})
return result
def classify_api_sensitivity(apis):
"""Classify APIs by data sensitivity level."""
sensitive_patterns = {
"PII": [r"/users?(/|$)", r"/customers?(/|$)", r"/profile", r"/account"],
"Financial": [r"/payments?", r"/billing", r"/invoices?", r"/transactions?"],
"Auth": [r"/auth", r"/login", r"/token", r"/oauth", r"/password"],
"Admin": [r"/admin", r"/management", r"/config", r"/settings"],
"Health": [r"/health", r"/status", r"/metrics", r"/ping"],
}
classified = []
for api in apis:
path = api["path"]
categories = []
for category, patterns in sensitive_patterns.items():
if any(re.search(p, path, re.IGNORECASE) for p in patterns):
categories.append(category)
sensitivity = "HIGH" if any(c in categories for c in ["PII", "Financial", "Auth", "Admin"]) \
else "LOW" if "Health" in categories else "MEDIUM"
classified.append({**api, "categories": categories, "sensitivity": sensitivity})
return classified
def score_api_risk(apis):
"""Calculate risk score for each API endpoint."""
scored = []
for api in apis:
risk_score = 0
factors = []
if api.get("sensitivity") == "HIGH":
risk_score += 30
factors.append("high_sensitivity_data")
if api.get("error_rate", 0) > 0.1:
risk_score += 20
factors.append("high_error_rate")
if api.get("unique_consumers", 0) > 100:
risk_score += 10
factors.append("high_consumer_count")
if "Auth" in api.get("categories", []):
risk_score += 15
factors.append("authentication_endpoint")
methods = api.get("methods", [])
if any(m in methods for m in ["DELETE", "PUT", "PATCH"]):
risk_score += 10
factors.append("state_changing_methods")
severity = "CRITICAL" if risk_score >= 50 else "HIGH" if risk_score >= 30 else "MEDIUM"
scored.append({**api, "risk_score": risk_score, "risk_factors": factors,
"risk_level": severity})
return sorted(scored, key=lambda x: x["risk_score"], reverse=True)
def check_api_security_controls(apis, spec_path=None):
"""Check security controls for discovered APIs."""
findings = []
spec_paths = set()
if spec_path:
try:
import yaml
with open(spec_path) as f:
spec = yaml.safe_load(f) if spec_path.endswith((".yaml", ".yml")) else json.load(f)
spec_paths = set(spec.get("paths", {}).keys())
except Exception:
pass
for api in apis:
if spec_paths and api["path"] not in spec_paths:
findings.append({
"path": api["path"], "issue": "undocumented_api",
"severity": "HIGH", "recommendation": "Add to OpenAPI spec or deprecate",
})
if api.get("sensitivity") == "HIGH" and api.get("error_rate", 0) > 0.05:
findings.append({
"path": api["path"], "issue": "sensitive_endpoint_high_errors",
"severity": "HIGH",
})
return findings
def main():
parser = argparse.ArgumentParser(description="API Security Posture Management Agent")
parser.add_argument("--log", help="API traffic log (JSON lines)")
parser.add_argument("--spec", help="OpenAPI spec for comparison")
parser.add_argument("--output", default="api_posture_report.json")
parser.add_argument("--action", choices=["discover", "classify", "score", "audit", "full"],
default="full")
args = parser.parse_args()
report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}}
if args.log:
apis = discover_apis_from_traffic(args.log)
report["findings"]["discovered_apis"] = len(apis)
print(f"[+] Discovered {len(apis)} API endpoints")
if args.action in ("classify", "full"):
apis = classify_api_sensitivity(apis)
if args.action in ("score", "full"):
apis = score_api_risk(apis)
report["findings"]["api_inventory"] = apis[:200]
if args.action in ("audit", "full"):
f = check_api_security_controls(apis, args.spec)
report["findings"]["security_gaps"] = f
print(f"[+] Security gaps: {len(f)}")
with open(args.output, "w") as fout:
json.dump(report, fout, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()